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:
Diffstat (limited to 'source/blender/gpencil_modifiers/intern/lineart')
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h302
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c182
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c1028
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h94
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c1419
6 files changed, 2637 insertions, 390 deletions
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index a72ce76d591..ae013a7dd02 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -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,81 @@ typedef struct LineartElementLinkNode {
void *object_ref;
eLineArtElementNodeFlag flags;
+ /* For edge element link nodes, used for shadow edge matching. */
+ int obindex;
+ int global_index_offset;
+
/** 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 frame-buffer 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 calculation 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 explanation. */
+ 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 +164,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 +195,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 +204,14 @@ typedef struct LineartEdgeChain {
int loop_id;
uint8_t material_mask_bits;
uint8_t intersection_mask;
+ uint32_t shadow_mask_bits;
+
+ /* We need local index for correct weight transfer, line art index is global, thus
+ * local_index=lineart_index-index_offset. */
+ uint32_t index_offset;
struct Object *object_ref;
+ struct Object *silhouette_backdrop;
} LineartEdgeChain;
typedef struct LineartEdgeChainItem {
@@ -167,9 +222,10 @@ typedef struct LineartEdgeChainItem {
float gpos[3];
float normal[3];
uint16_t line_type;
- int8_t occlusion;
+ uint8_t occlusion;
uint8_t material_mask_bits;
uint8_t intersection_mask;
+ uint32_t shadow_mask_bits;
size_t index;
} LineartEdgeChainItem;
@@ -201,6 +257,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 +277,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 available 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 +299,7 @@ typedef struct LineartData {
struct LineartBoundingArea *initials;
- uint32_t tile_count;
+ uint32_t initial_tile_count;
} qtree;
@@ -267,6 +336,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 +366,22 @@ 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;
+ /* "Secondary" ones are from viewing camera
+ * (as opposed to shadow camera), during shadow calculation. */
+ bool cam_is_persp_secondary;
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,34 +389,44 @@ typedef struct LineartData {
float chain_smooth_tolerance;
double view_vector[3];
-
+ double view_vector_secondary[3]; /* For shadow. */
} conf;
LineartElementLinkNode *isect_scheduled_up_to;
int isect_scheduled_up_to_index;
- /* Note: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */
+ /* NOTE: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */
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. */
- int8_t rb_edge_types;
+ uint16_t all_enabled_edge_types;
} LineartCache;
#define DBL_TRIANGLE_LIM 1e-8
@@ -348,6 +444,19 @@ typedef enum eLineartTriangleFlags {
LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5),
} eLineartTriangleFlags;
+#define LRT_SHADOW_MASK_UNDEFINED 0
+#define LRT_SHADOW_MASK_ILLUMINATED (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)
+#define LRT_SHADOW_MASK_ILLUMINATED_SHAPE (1 << 6)
+
+#define LRT_SHADOW_TEST_SHAPE_BITS \
+ (LRT_SHADOW_MASK_ILLUMINATED | LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_INHIBITED | \
+ LRT_SHADOW_MASK_ILLUMINATED_SHAPE)
+
/**
* Controls how many edges a worker thread is processing at one request.
* There's no significant performance impact on choosing different values.
@@ -368,9 +477,18 @@ 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;
+ struct Object *original_ob_eval; /* For evaluated materials */
struct Mesh *original_me;
double model_view_proj[4][4];
double model_view[4][4];
@@ -378,11 +496,15 @@ 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. */
+ /** NOTE: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */
struct LineartPendingEdges pending_edges;
} LineartObjectInfo;
@@ -394,6 +516,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;
/**
@@ -438,7 +561,7 @@ typedef struct LineartBoundingArea {
uint32_t max_triangle_count;
uint32_t line_count;
uint32_t max_line_count;
- uint32_t user_count;
+ uint32_t insider_triangle_count;
/* Use array for speeding up multiple accesses. */
struct LineartTriangle **linked_triangles;
@@ -466,6 +589,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 +605,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 +749,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 further
+ * 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;
@@ -637,7 +857,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld);
* implemented yet.
*/
void MOD_lineart_chain_connect(LineartData *ld);
-void MOD_lineart_chain_discard_short(LineartData *ld, float threshold);
+void MOD_lineart_chain_discard_unused(LineartData *ld, float threshold, uint8_t max_occlusion);
void MOD_lineart_chain_clip_at_border(LineartData *ld);
/**
* This should always be the last stage!, see the end of
@@ -646,9 +866,11 @@ 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);
+void MOD_lineart_finalize_chains(LineartData *ld);
/**
* This is the entry point of all line art calculations.
@@ -694,6 +916,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);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 1d84bb8e232..f32141a31eb 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -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,13 +718,16 @@ 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;
}
}
}
- /* Get rid of those very short "zig-zag" lines that jumps around visibility. */
- MOD_lineart_chain_discard_short(ld, DBL_EDGE_LIM);
+
+ MOD_lineart_chain_discard_unused(ld, DBL_EDGE_LIM, ld->conf.max_occlusion_level);
+
LISTBASE_FOREACH (LineartEdgeChain *, iec, &ld->chains) {
lineart_bounding_area_link_chain(ld, iec);
}
@@ -749,6 +793,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 +824,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 +871,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 +902,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 +918,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 +931,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 +944,7 @@ void MOD_lineart_chain_connect(LineartData *ld)
occlusion,
material_mask_bits,
isec_mask,
+ shadow_mask,
loop_id,
dist,
&dist_l,
@@ -907,6 +956,7 @@ void MOD_lineart_chain_connect(LineartData *ld)
occlusion,
material_mask_bits,
isec_mask,
+ shadow_mask,
loop_id,
dist,
&dist_r,
@@ -969,12 +1019,14 @@ float MOD_lineart_chain_compute_length(LineartEdgeChain *ec)
return offset_accum;
}
-void MOD_lineart_chain_discard_short(LineartData *ld, const float threshold)
+void MOD_lineart_chain_discard_unused(LineartData *ld,
+ const float threshold,
+ uint8_t max_occlusion)
{
LineartEdgeChain *ec, *next_ec;
for (ec = ld->chains.first; ec; ec = next_ec) {
next_ec = ec->next;
- if (MOD_lineart_chain_compute_length(ec) < threshold) {
+ if (ec->level > max_occlusion || MOD_lineart_chain_compute_length(ec) < threshold) {
BLI_remlink(&ld->chains, ec);
}
}
@@ -999,6 +1051,38 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
}
}
+LineartElementLinkNode *lineart_find_matching_eln_obj(ListBase *elns, struct Object *obj)
+{
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, elns) {
+ if (eln->object_ref == obj) {
+ return eln;
+ }
+ }
+ return NULL;
+}
+
+void MOD_lineart_finalize_chains(LineartData *ld)
+{
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) {
+ if (ELEM(ec->type,
+ LRT_EDGE_FLAG_INTERSECTION,
+ LRT_EDGE_FLAG_PROJECTED_SHADOW,
+ LRT_EDGE_FLAG_LIGHT_CONTOUR)) {
+ continue;
+ }
+ LineartElementLinkNode *eln = lineart_find_matching_eln_obj(&ld->geom.vertex_buffer_pointers,
+ ec->object_ref);
+ BLI_assert(eln != NULL);
+ if (LIKELY(eln)) {
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ if (eci->index > eln->global_index_offset) {
+ eci->index -= eln->global_index_offset;
+ }
+ }
+ }
+ }
+}
+
void MOD_lineart_smooth_chains(LineartData *ld, float tolerance)
{
LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) {
@@ -1072,20 +1156,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 +1212,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 +1305,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 +1313,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 +1358,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;
+ }
+ }
+}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index c08bf3e0fe9..c4a235d06bc 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -39,6 +39,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"
@@ -49,7 +50,7 @@
#include "lineart_intern.h"
typedef struct LineartIsecSingle {
- float v1[3], v2[3];
+ double v1[3], v2[3];
LineartTriangle *tri1, *tri2;
} LineartIsecSingle;
@@ -68,7 +69,7 @@ typedef struct LineartIsecThread {
int max;
int count_test;
- /* For individual thread reference.*/
+ /* For individual thread reference. */
LineartData *ld;
} LineartIsecThread;
@@ -78,44 +79,29 @@ typedef struct LineartIsecData {
int thread_count;
} LineartIsecData;
-static LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld, LineartEdge *e);
-
static void lineart_bounding_area_link_edge(LineartData *ld,
LineartBoundingArea *root_ba,
LineartEdge *e);
-static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this,
- LineartEdge *e,
- double x,
- double y,
- double k,
- int positive_x,
- int positive_y,
- double *next_x,
- double *next_y);
-
static bool lineart_get_edge_bounding_areas(
LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend);
-static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl,
- const LineartTriangle *tri,
+static bool lineart_triangle_edge_image_space_occlusion(const LineartTriangle *tri,
const LineartEdge *e,
const double *override_camera_loc,
const bool override_cam_is_persp,
const bool allow_overlapping_edges,
- const double vp[4][4],
- const double *camera_dir,
+ const double m_view_projection[4][4],
+ const double camera_dir[3],
const float cam_shift_x,
const float cam_shift_y,
double *from,
double *to);
-static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e);
-
static void lineart_bounding_area_link_triangle(LineartData *ld,
LineartBoundingArea *root_ba,
LineartTriangle *tri,
- double *LRUB,
+ double l_r_u_b[4],
int recursive,
int recursive_level,
bool do_intersection,
@@ -154,19 +140,20 @@ static LineartEdgeSegment *lineart_give_segment(LineartData *ld)
BLI_spin_unlock(&ld->lock_cuts);
/* Otherwise allocate some new memory. */
- return (LineartEdgeSegment *)lineart_mem_acquire_thread(&ld->render_data_pool,
+ return (LineartEdgeSegment *)lineart_mem_acquire_thread(ld->edge_data_pool,
sizeof(LineartEdgeSegment));
}
/**
* Cuts the edge in image space and mark occlusion level for each segment.
*/
-static void lineart_edge_cut(LineartData *ld,
- LineartEdge *e,
- double start,
- double end,
- uchar material_mask_bits,
- uchar mat_occlusion)
+void lineart_edge_cut(LineartData *ld,
+ LineartEdge *e,
+ double start,
+ double end,
+ uchar material_mask_bits,
+ uchar mat_occlusion,
+ uint32_t shadow_bits)
{
LineartEdgeSegment *seg, *i_seg, *next_seg, *prev_seg;
LineartEdgeSegment *cut_start_before = 0, *cut_end_before = 0;
@@ -198,7 +185,7 @@ static void lineart_edge_cut(LineartData *ld,
/* Not using a list iteration macro because of it more clear when using for loops to iterate
* through the segments. */
for (seg = e->segments.first; seg; seg = seg->next) {
- if (LRT_DOUBLE_CLOSE_ENOUGH(seg->at, start)) {
+ if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, start)) {
cut_start_before = seg;
new_seg1 = cut_start_before;
break;
@@ -207,7 +194,7 @@ static void lineart_edge_cut(LineartData *ld,
break;
}
i_seg = seg->next;
- if (i_seg->at > start + 1e-09 && start > seg->at) {
+ if (i_seg->ratio > start + 1e-09 && start > seg->ratio) {
cut_start_before = i_seg;
new_seg1 = lineart_give_segment(ld);
break;
@@ -217,15 +204,15 @@ static void lineart_edge_cut(LineartData *ld,
untouched = 1;
}
for (seg = cut_start_before; seg; seg = seg->next) {
- /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle
+ /* We tried to cut ratio existing cutting point (e.g. where the line's occluded by a triangle
* strip). */
- if (LRT_DOUBLE_CLOSE_ENOUGH(seg->at, end)) {
+ if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, end)) {
cut_end_before = seg;
new_seg2 = cut_end_before;
break;
}
- /* This check is to prevent `es->at == 1.0` (where we don't need to cut because we are at the
- * end point). */
+ /* This check is to prevent `es->ratio == 1.0` (where we don't need to cut because we are ratio
+ * the end point). */
if (!seg->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
cut_end_before = seg;
new_seg2 = cut_end_before;
@@ -233,7 +220,7 @@ static void lineart_edge_cut(LineartData *ld,
break;
}
/* When an actual cut is needed in the line. */
- if (seg->at > end) {
+ if (seg->ratio > end) {
cut_end_before = seg;
new_seg2 = lineart_give_segment(ld);
break;
@@ -261,6 +248,7 @@ static void lineart_edge_cut(LineartData *ld,
if (i_seg) {
new_seg1->occlusion = i_seg->occlusion;
new_seg1->material_mask_bits = i_seg->material_mask_bits;
+ new_seg1->shadow_mask_bits = i_seg->shadow_mask_bits;
}
BLI_insertlinkbefore(&e->segments, cut_start_before, new_seg1);
}
@@ -272,6 +260,7 @@ static void lineart_edge_cut(LineartData *ld,
i_seg = e->segments.last;
new_seg1->occlusion = i_seg->occlusion;
new_seg1->material_mask_bits = i_seg->material_mask_bits;
+ new_seg1->shadow_mask_bits = i_seg->shadow_mask_bits;
BLI_addtail(&e->segments, new_seg1);
}
if (cut_end_before) {
@@ -281,6 +270,7 @@ static void lineart_edge_cut(LineartData *ld,
if (i_seg) {
new_seg2->occlusion = i_seg->occlusion;
new_seg2->material_mask_bits = i_seg->material_mask_bits;
+ new_seg2->shadow_mask_bits = i_seg->shadow_mask_bits;
}
BLI_insertlinkbefore(&e->segments, cut_end_before, new_seg2);
}
@@ -289,14 +279,17 @@ static void lineart_edge_cut(LineartData *ld,
i_seg = e->segments.last;
new_seg2->occlusion = i_seg->occlusion;
new_seg2->material_mask_bits = i_seg->material_mask_bits;
- BLI_addtail(&e->segments, new_seg2);
+ new_seg2->shadow_mask_bits = i_seg->shadow_mask_bits;
+ if (!untouched) {
+ BLI_addtail(&e->segments, new_seg2);
+ }
}
/* If we touched the cut list, we assign the new cut position based on new cut position,
* this way we accommodate precision lost due to multiple cut inserts. */
- new_seg1->at = start;
+ new_seg1->ratio = start;
if (!untouched) {
- new_seg2->at = end;
+ new_seg2->ratio = end;
}
else {
/* For the convenience of the loop below. */
@@ -307,6 +300,21 @@ static void lineart_edge_cut(LineartData *ld,
for (seg = new_seg1; seg && seg != new_seg2; seg = seg->next) {
seg->occlusion += mat_occlusion;
seg->material_mask_bits |= material_mask_bits;
+
+ /* The enclosed shape flag will override regular lit/shaded
+ * flags. See LineartEdgeSegment::shadow_mask_bits for details. */
+ if (shadow_bits == LRT_SHADOW_MASK_ENCLOSED_SHAPE) {
+ if (seg->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED ||
+ e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) {
+ seg->shadow_mask_bits |= LRT_SHADOW_MASK_INHIBITED;
+ }
+ else if (seg->shadow_mask_bits & LRT_SHADOW_MASK_SHADED) {
+ seg->shadow_mask_bits |= LRT_SHADOW_MASK_ILLUMINATED_SHAPE;
+ }
+ }
+ else {
+ seg->shadow_mask_bits |= shadow_bits;
+ }
}
/* Reduce adjacent cutting points of the same level, which saves memory. */
@@ -316,7 +324,8 @@ static void lineart_edge_cut(LineartData *ld,
next_seg = seg->next;
if (prev_seg && prev_seg->occlusion == seg->occlusion &&
- prev_seg->material_mask_bits == seg->material_mask_bits) {
+ prev_seg->material_mask_bits == seg->material_mask_bits &&
+ prev_seg->shadow_mask_bits == seg->shadow_mask_bits) {
BLI_remlink(&e->segments, seg);
/* This puts the node back to the render buffer, if more cut happens, these unused nodes get
* picked first. */
@@ -336,10 +345,8 @@ static void lineart_edge_cut(LineartData *ld,
*/
BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *tri)
{
- LineartVertIntersection *v1 = (void *)e->v1;
- LineartVertIntersection *v2 = (void *)e->v2;
- return ((v1->base.flag && v1->intersecting_with == tri) ||
- (v2->base.flag && v2->intersecting_with == tri));
+ return (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) ||
+ (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference));
}
static void lineart_bounding_area_triangle_reallocate(LineartBoundingArea *ba)
@@ -371,24 +378,10 @@ static void lineart_bounding_area_line_add(LineartBoundingArea *ba, LineartEdge
static void lineart_occlusion_single_line(LineartData *ld, LineartEdge *e, int thread_id)
{
- double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1];
- LineartBoundingArea *ba = lineart_edge_first_bounding_area(ld, e);
- LineartBoundingArea *nba = ba;
LineartTriangleThread *tri;
-
- /* These values are used for marching along the line. */
double l, r;
- double k = (e->v2->fbcoord[1] - e->v1->fbcoord[1]) /
- (e->v2->fbcoord[0] - e->v1->fbcoord[0] + 1e-30);
- int positive_x = (e->v2->fbcoord[0] - e->v1->fbcoord[0]) > 0 ?
- 1 :
- (e->v2->fbcoord[0] == e->v1->fbcoord[0] ? 0 : -1);
- int positive_y = (e->v2->fbcoord[1] - e->v1->fbcoord[1]) > 0 ?
- 1 :
- (e->v2->fbcoord[1] == e->v1->fbcoord[1] ? 0 : -1);
-
- while (nba) {
-
+ LRT_EDGE_BA_MARCHING_BEGIN(e->v1->fbcoord, e->v2->fbcoord)
+ {
for (int i = 0; i < nba->triangle_count; i++) {
tri = (LineartTriangleThread *)nba->linked_triangles[i];
/* If we are already testing the line in this thread, then don't do it. */
@@ -401,8 +394,7 @@ static void lineart_occlusion_single_line(LineartData *ld, LineartEdge *e, int t
continue;
}
tri->testing_e[thread_id] = e;
- if (lineart_triangle_edge_image_space_occlusion(&ld->lock_task,
- (const LineartTriangle *)tri,
+ if (lineart_triangle_edge_image_space_occlusion((const LineartTriangle *)tri,
e,
ld->conf.camera_pos,
ld->conf.cam_is_persp,
@@ -413,7 +405,7 @@ static void lineart_occlusion_single_line(LineartData *ld, LineartEdge *e, int t
ld->conf.shift_y,
&l,
&r)) {
- lineart_edge_cut(ld, e, l, r, tri->base.material_mask_bits, tri->base.mat_occlusion);
+ lineart_edge_cut(ld, e, l, r, tri->base.material_mask_bits, tri->base.mat_occlusion, 0);
if (e->min_occ > ld->conf.max_occlusion_level) {
/* No need to calculate any longer on this line because no level more than set value is
* going to show up in the rendered result. */
@@ -421,9 +413,9 @@ static void lineart_occlusion_single_line(LineartData *ld, LineartEdge *e, int t
}
}
}
- /* Marching along `e->v1` to `e->v2`, searching each possible bounding areas it may touch. */
- nba = lineart_bounding_area_next(nba, e, x, y, k, positive_x, positive_y, &x, &y);
+ LRT_EDGE_BA_MARCHING_NEXT(e->v1->fbcoord, e->v2->fbcoord)
}
+ LRT_EDGE_BA_MARCHING_END
}
static int lineart_occlusion_make_task_info(LineartData *ld, LineartRenderTaskInfo *rti)
@@ -469,7 +461,7 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR
* #MOD_lineart_compute_feature_lines function.
* This function handles all occlusion calculation.
*/
-static void lineart_main_occlusion_begin(LineartData *ld)
+void lineart_main_occlusion_begin(LineartData *ld)
{
int thread_count = ld->thread_count;
LineartRenderTaskInfo *rti = MEM_callocN(sizeof(LineartRenderTaskInfo) * thread_count,
@@ -501,10 +493,10 @@ static bool lineart_point_inside_triangle(const double v[2],
const double v1[2],
const double v2[2])
{
- double cl, c;
+ double cl, c, cl0;
cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
- c = cl;
+ c = cl0 = cl;
cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]);
if (c * cl <= 0) {
@@ -520,8 +512,7 @@ static bool lineart_point_inside_triangle(const double v[2],
c = cl;
- cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
- if (c * cl <= 0) {
+ if (c * cl0 <= 0) {
return false;
}
@@ -703,10 +694,10 @@ static LineartElementLinkNode *lineart_memory_get_edge_space(LineartData *ld)
{
LineartElementLinkNode *eln;
- LineartEdge *render_edges = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdge) * 64);
+ LineartEdge *render_edges = lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdge) * 64);
eln = lineart_list_append_pointer_pool_sized(&ld->geom.line_buffer_pointers,
- &ld->render_data_pool,
+ ld->edge_data_pool,
render_edges,
sizeof(LineartElementLinkNode));
eln->element_count = 64;
@@ -724,6 +715,8 @@ static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig)
tri->intersection_mask = orig->intersection_mask;
tri->material_mask_bits = orig->material_mask_bits;
tri->mat_occlusion = orig->mat_occlusion;
+ tri->intersection_priority = orig->intersection_priority;
+ tri->target_reference = orig->target_reference;
}
static void lineart_triangle_set_cull_flag(LineartTriangle *tri, uchar flag)
@@ -757,10 +750,10 @@ static void lineart_triangle_cull_single(LineartData *ld,
int in0,
int in1,
int in2,
- double *cam_pos,
- double *view_dir,
+ double cam_pos[3],
+ double view_dir[3],
bool allow_boundaries,
- double (*vp)[4],
+ double m_view_projection[4][4],
Object *ob,
int *r_v_count,
int *r_e_count,
@@ -887,7 +880,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
a = dot_v1 / (dot_v1 + dot_v2);
/* Assign it to a new point. */
interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
- mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
vt[0].index = tri->v[2]->index;
/* Cut point for line 1---|-----0. */
@@ -898,7 +891,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
a = dot_v1 / (dot_v1 + dot_v2);
/* Assign it to another new point. */
interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
- mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
vt[1].index = tri->v[1]->index;
/* New line connecting two new points. */
@@ -939,7 +932,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
- mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
vt[0].index = tri->v[0]->index;
sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos);
@@ -948,7 +941,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
- mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
vt[1].index = tri->v[1]->index;
INCREASE_EDGE
@@ -980,7 +973,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
- mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
vt[0].index = tri->v[2]->index;
sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos);
@@ -989,7 +982,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
- mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
vt[1].index = tri->v[0]->index;
INCREASE_EDGE
@@ -1052,7 +1045,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
a = dot_v2 / (dot_v1 + dot_v2);
/* Assign to a new point. */
interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
- mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
vt[0].index = tri->v[0]->index;
/* Cut point for line 0---|------2. */
@@ -1063,7 +1056,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
a = dot_v2 / (dot_v1 + dot_v2);
/* Assign to other new point. */
interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
- mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
vt[1].index = tri->v[0]->index;
/* New line connects two new points. */
@@ -1107,7 +1100,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
- mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
vt[0].index = tri->v[1]->index;
sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos);
@@ -1116,7 +1109,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
- mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
vt[1].index = tri->v[1]->index;
INCREASE_EDGE
@@ -1156,7 +1149,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
- mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
vt[0].index = tri->v[2]->index;
sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos);
@@ -1165,7 +1158,7 @@ static void lineart_triangle_cull_single(LineartData *ld,
dot_v2 = dot_v3v3_db(span_v2, view_dir);
a = dot_v1 / (dot_v1 + dot_v2);
interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
- mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
vt[1].index = tri->v[2]->index;
INCREASE_EDGE
@@ -1215,11 +1208,11 @@ static void lineart_triangle_cull_single(LineartData *ld,
* new topology that represents the trimmed triangle. (which then became a triangle or a square
* formed by two triangles)
*/
-static void lineart_main_cull_triangles(LineartData *ld, bool clip_far)
+void lineart_main_cull_triangles(LineartData *ld, bool clip_far)
{
LineartTriangle *tri;
LineartElementLinkNode *v_eln, *t_eln, *e_eln;
- double(*vp)[4] = ld->conf.view_projection;
+ double(*m_view_projection)[4] = ld->conf.view_projection;
int i;
int v_count = 0, t_count = 0, e_count = 0;
Object *ob;
@@ -1329,7 +1322,7 @@ static void lineart_main_cull_triangles(LineartData *ld, bool clip_far)
cam_pos,
view_dir,
allow_boundaries,
- vp,
+ m_view_projection,
ob,
&v_count,
&e_count,
@@ -1350,7 +1343,7 @@ static void lineart_main_cull_triangles(LineartData *ld, bool clip_far)
* Adjacent data is only used during the initial stages of computing.
* So we can free it using this function when it is not needed anymore.
*/
-static void lineart_main_free_adjacent_data(LineartData *ld)
+void lineart_main_free_adjacent_data(LineartData *ld)
{
LinkData *link;
while ((link = BLI_pophead(&ld->geom.triangle_adjacent_pointers)) != NULL) {
@@ -1368,7 +1361,7 @@ static void lineart_main_free_adjacent_data(LineartData *ld)
}
}
-static void lineart_main_perspective_division(LineartData *ld)
+void lineart_main_perspective_division(LineartData *ld)
{
LineartVert *vt;
int i;
@@ -1393,7 +1386,7 @@ static void lineart_main_perspective_division(LineartData *ld)
}
}
-static void lineart_main_discard_out_of_frame_edges(LineartData *ld)
+void lineart_main_discard_out_of_frame_edges(LineartData *ld)
{
LineartEdge *e;
int i;
@@ -1418,7 +1411,7 @@ typedef struct LineartEdgeNeighbor {
} LineartEdgeNeighbor;
typedef struct VertData {
- MVert *mvert;
+ const MVert *mvert;
LineartVert *v_arr;
double (*model_view)[4];
double (*model_view_proj)[4];
@@ -1429,7 +1422,7 @@ static void lineart_mvert_transform_task(void *__restrict userdata,
const TaskParallelTLS *__restrict UNUSED(tls))
{
VertData *vert_task_data = (VertData *)userdata;
- MVert *m_v = &vert_task_data->mvert[i];
+ const MVert *m_v = &vert_task_data->mvert[i];
double co[4];
LineartVert *v = &vert_task_data->v_arr[i];
copy_v3db_v3fl(co, m_v->co);
@@ -1444,9 +1437,10 @@ static const int LRT_MESH_EDGE_TYPES[] = {
LRT_EDGE_FLAG_CREASE,
LRT_EDGE_FLAG_MATERIAL,
LRT_EDGE_FLAG_LOOSE,
+ LRT_EDGE_FLAG_CONTOUR_SECONDARY,
};
-#define LRT_MESH_EDGE_TYPES_COUNT 5
+#define LRT_MESH_EDGE_TYPES_COUNT 6
static int lineart_edge_type_duplication_count(int eflag)
{
@@ -1476,7 +1470,9 @@ static LineartTriangle *lineart_triangle_from_index(LineartData *ld,
typedef struct EdgeFeatData {
LineartData *ld;
Mesh *me;
+ Object *ob_eval; /* For evaluated materials. */
const MLoopTri *mlooptri;
+ const int *material_indices;
LineartTriangle *tri_array;
LineartVert *v_array;
float crease_threshold;
@@ -1508,6 +1504,8 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata;
EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk;
Mesh *me = e_feat_data->me;
+ const int *material_indices = e_feat_data->material_indices;
+ Object *ob_eval = e_feat_data->ob_eval;
LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr;
const MLoopTri *mlooptri = e_feat_data->mlooptri;
@@ -1622,13 +1620,31 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
}
}
+ if (ld->conf.use_contour_secondary) {
+ view_vector = view_vector_persp;
+ if (ld->conf.cam_is_persp_secondary) {
+ sub_v3_v3v3_db(view_vector, vert->gloc, ld->conf.camera_pos_secondary);
+ }
+ else {
+ view_vector = ld->conf.view_vector_secondary;
+ }
+
+ dot_v1 = dot_v3v3_db(view_vector, tri1->gn);
+ dot_v2 = dot_v3v3_db(view_vector, tri2->gn);
+
+ if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) {
+ edge_flag_result |= LRT_EDGE_FLAG_CONTOUR_SECONDARY;
+ }
+ }
+
if (!only_contour) {
+ const MPoly *polys = BKE_mesh_polys(me);
if (ld->conf.use_crease) {
bool do_crease = true;
if (!ld->conf.force_crease && !e_feat_data->use_auto_smooth &&
- (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) &&
- (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) {
+ (polys[mlooptri[f1].poly].flag & ME_SMOOTH) &&
+ (polys[mlooptri[f2].poly].flag & ME_SMOOTH)) {
do_crease = false;
}
if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) {
@@ -1636,11 +1652,22 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
}
}
- int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr;
- int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr;
+ int mat1 = material_indices ? material_indices[mlooptri[f1].poly] : 0;
+ int mat2 = material_indices ? material_indices[mlooptri[f2].poly] : 0;
- if (ld->conf.use_material && mat1 != mat2) {
- edge_flag_result |= LRT_EDGE_FLAG_MATERIAL;
+ if (mat1 != mat2) {
+ Material *m1 = BKE_object_material_get_eval(ob_eval, mat1 + 1);
+ Material *m2 = BKE_object_material_get_eval(ob_eval, mat2 + 1);
+ if (m1 && m2 &&
+ ((m1->lineart.mat_occlusion == 0 && m2->lineart.mat_occlusion != 0) ||
+ (m2->lineart.mat_occlusion == 0 && m1->lineart.mat_occlusion != 0))) {
+ if (ld->conf.use_contour) {
+ edge_flag_result |= LRT_EDGE_FLAG_CONTOUR;
+ }
+ }
+ if (ld->conf.use_material) {
+ edge_flag_result |= LRT_EDGE_FLAG_MATERIAL;
+ }
}
}
else { /* only_contour */
@@ -1649,11 +1676,13 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
}
}
+ const MEdge *edges = BKE_mesh_edges(me);
+
int real_edges[3];
BKE_mesh_looptri_get_real_edges(me, &mlooptri[i / 3], real_edges);
if (real_edges[i % 3] >= 0) {
- MEdge *medge = &me->medge[real_edges[i % 3]];
+ const MEdge *medge = &edges[real_edges[i % 3]];
if (ld->conf.use_crease && ld->conf.sharp_as_crease && (medge->flag & ME_SHARP)) {
edge_flag_result |= LRT_EDGE_FLAG_CREASE;
@@ -1684,16 +1713,16 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
typedef struct LooseEdgeData {
int loose_count;
int loose_max;
- MEdge **loose_array;
- Mesh *me;
+ int *loose_array;
+ const MEdge *edges;
} LooseEdgeData;
static void lineart_loose_data_reallocate(LooseEdgeData *loose_data, int count)
{
- MEdge **new_arr = MEM_callocN(sizeof(MEdge *) * count, "loose edge array");
+ int *new_arr = MEM_calloc_arrayN(count, sizeof(int), "loose edge array");
if (loose_data->loose_array) {
- memcpy(new_arr, loose_data->loose_array, sizeof(MEdge *) * loose_data->loose_max);
- MEM_freeN(loose_data->loose_array);
+ memcpy(new_arr, loose_data->loose_array, sizeof(int) * loose_data->loose_max);
+ MEM_SAFE_FREE(loose_data->loose_array);
}
loose_data->loose_max = count;
loose_data->loose_array = new_arr;
@@ -1710,19 +1739,19 @@ static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData
}
memcpy(&loose_data->loose_array[loose_data->loose_count],
to_be_joined->loose_array,
- sizeof(MEdge *) * to_be_joined->loose_count);
+ sizeof(int) * to_be_joined->loose_count);
loose_data->loose_count += to_be_joined->loose_count;
MEM_freeN(to_be_joined->loose_array);
to_be_joined->loose_array = NULL;
}
-static void lineart_add_loose_edge(LooseEdgeData *loose_data, MEdge *e)
+static void lineart_add_loose_edge(LooseEdgeData *loose_data, const int i)
{
if (loose_data->loose_count >= loose_data->loose_max) {
int min_amount = MAX2(100, loose_data->loose_count * 2);
lineart_loose_data_reallocate(loose_data, min_amount);
}
- loose_data->loose_array[loose_data->loose_count] = e;
+ loose_data->loose_array[loose_data->loose_count] = i;
loose_data->loose_count++;
}
@@ -1731,10 +1760,9 @@ static void lineart_identify_loose_edges(void *__restrict UNUSED(userdata),
const TaskParallelTLS *__restrict tls)
{
LooseEdgeData *loose_data = (LooseEdgeData *)tls->userdata_chunk;
- Mesh *me = loose_data->me;
- if (me->medge[i].flag & ME_LOOSEEDGE) {
- lineart_add_loose_edge(loose_data, &me->medge[i]);
+ if (loose_data->edges[i].flag & ME_LOOSEEDGE) {
+ lineart_add_loose_edge(loose_data, i);
}
}
@@ -1747,7 +1775,7 @@ static void loose_data_sum_reduce(const void *__restrict UNUSED(userdata),
lineart_join_loose_edge_arr(final, loose_chunk);
}
-static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e)
+void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e)
{
if (pe->next >= pe->max || !pe->max) {
if (!pe->max) {
@@ -1766,17 +1794,16 @@ static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e)
pe->array[pe->next] = e;
pe->next++;
}
-
static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge *e)
{
lineart_add_edge_to_array(&obi->pending_edges, e);
}
-/* Note: For simplicity, this function doesn't actually do anything if you already have data in
- * #pe. */
-static void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count)
+/* NOTE: For simplicity, this function doesn't actually do anything if you already have data in
+ * #pe. */
+void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count)
{
- if (pe->max || pe->array) {
+ if (pe->max || pe->array || count == 0) {
return;
}
@@ -1818,6 +1845,7 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri,
typedef struct TriData {
LineartObjectInfo *ob_info;
const MLoopTri *mlooptri;
+ const int *material_indices;
LineartVert *vert_arr;
LineartTriangle *tri_arr;
int lineart_triangle_size;
@@ -1832,35 +1860,44 @@ static void lineart_load_tri_task(void *__restrict userdata,
Mesh *me = tri_task_data->ob_info->original_me;
LineartObjectInfo *ob_info = tri_task_data->ob_info;
const MLoopTri *mlooptri = &tri_task_data->mlooptri[i];
+ const int *material_indices = tri_task_data->material_indices;
LineartVert *vert_arr = tri_task_data->vert_arr;
LineartTriangle *tri = tri_task_data->tri_arr;
+ const MLoop *loops = BKE_mesh_loops(me);
tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i);
- int v1 = me->mloop[mlooptri->tri[0]].v;
- int v2 = me->mloop[mlooptri->tri[1]].v;
- int v3 = me->mloop[mlooptri->tri[2]].v;
+ int v1 = loops[mlooptri->tri[0]].v;
+ int v2 = loops[mlooptri->tri[1]].v;
+ int v3 = loops[mlooptri->tri[2]].v;
tri->v[0] = &vert_arr[v1];
tri->v[1] = &vert_arr[v2];
tri->v[2] = &vert_arr[v3];
/* Material mask bits and occlusion effectiveness assignment. */
- Material *mat = BKE_object_material_get(ob_info->original_ob,
- me->mpoly[mlooptri->poly].mat_nr + 1);
+ Material *mat = BKE_object_material_get(
+ ob_info->original_ob_eval, material_indices ? material_indices[mlooptri->poly] + 1 : 1);
tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
mat->lineart.material_mask_bits :
0);
tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
+ tri->intersection_priority = ((mat && (mat->lineart.flags &
+ LRT_MATERIAL_CUSTOM_INTERSECTION_PRIORITY)) ?
+ mat->lineart.intersection_priority :
+ ob_info->intersection_priority);
tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
LRT_TRIANGLE_MAT_BACK_FACE_CULLING :
0;
tri->intersection_mask = ob_info->override_intersection_mask;
+ tri->target_reference = (ob_info->obindex | (i & LRT_OBINDEX_LOWER));
+
double gn[3];
float no[3];
- normal_tri_v3(no, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co);
+ const MVert *verts = BKE_mesh_verts(me);
+ normal_tri_v3(no, verts[v1].co, verts[v2].co, verts[v3].co);
copy_v3db_v3fl(gn, no);
mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn);
normalize_v3_db(tri->gn);
@@ -1879,8 +1916,8 @@ static void lineart_load_tri_task(void *__restrict userdata,
typedef struct EdgeNeighborData {
LineartEdgeNeighbor *edge_nabr;
LineartAdjacentEdge *adj_e;
- MLoopTri *mlooptri;
- MLoop *mloop;
+ const MLoopTri *mlooptri;
+ const MLoop *mloop;
} EdgeNeighborData;
static void lineart_edge_neighbor_init_task(void *__restrict userdata,
@@ -1889,9 +1926,9 @@ static void lineart_edge_neighbor_init_task(void *__restrict userdata,
{
EdgeNeighborData *en_data = (EdgeNeighborData *)userdata;
LineartAdjacentEdge *adj_e = &en_data->adj_e[i];
- MLoopTri *looptri = &en_data->mlooptri[i / 3];
+ const MLoopTri *looptri = &en_data->mlooptri[i / 3];
LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i];
- MLoop *mloop = en_data->mloop;
+ const MLoop *mloop = en_data->mloop;
adj_e->e = i;
adj_e->v1 = mloop[looptri->tri[i % 3]].v;
@@ -1925,7 +1962,7 @@ static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edge
en_data.adj_e = adj_e;
en_data.edge_nabr = edge_nabr;
en_data.mlooptri = mlooptri;
- en_data.mloop = me->mloop;
+ en_data.mloop = BKE_mesh_loops(me);
BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings);
@@ -1943,7 +1980,9 @@ static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edge
return edge_nabr;
}
-static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData *la_data)
+static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
+ LineartData *la_data,
+ ListBase *shadow_elns)
{
LineartElementLinkNode *elem_link_node;
LineartVert *la_v_arr;
@@ -1961,6 +2000,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me);
const int tot_tri = BKE_mesh_runtime_looptri_len(me);
+ const int *material_indices = (const int *)CustomData_get_layer_named(
+ &me->pdata, CD_PROP_INT32, "material_index");
+
/* Check if we should look for custom data tags like Freestyle edges or faces. */
bool can_find_freestyle_edge = false;
int layer_index = CustomData_get_active_layer_index(&me->edata, CD_FREESTYLE_EDGE);
@@ -1992,6 +2034,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
sizeof(LineartElementLinkNode));
BLI_spin_unlock(&la_data->lock_task);
+ elem_link_node->obindex = ob_info->obindex;
elem_link_node->element_count = me->totvert;
elem_link_node->object_ref = orig_ob;
ob_info->v_eln = elem_link_node;
@@ -2045,7 +2088,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
vert_settings.min_iter_per_thread = 4000;
VertData vert_data;
- vert_data.mvert = me->mvert;
+ vert_data.mvert = BKE_mesh_verts(me);
vert_data.v_arr = la_v_arr;
vert_data.model_view = ob_info->model_view;
vert_data.model_view_proj = ob_info->model_view_proj;
@@ -2063,6 +2106,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
TriData tri_data;
tri_data.ob_info = ob_info;
tri_data.mlooptri = mlooptri;
+ tri_data.material_indices = material_indices;
tri_data.vert_arr = la_v_arr;
tri_data.tri_arr = la_tri_arr;
tri_data.lineart_triangle_size = la_data->sizeof_triangle;
@@ -2088,7 +2132,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
EdgeFeatData edge_feat_data = {0};
edge_feat_data.ld = la_data;
edge_feat_data.me = me;
+ edge_feat_data.ob_eval = ob_info->original_ob_eval;
edge_feat_data.mlooptri = mlooptri;
+ edge_feat_data.material_indices = material_indices;
edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges);
edge_feat_data.tri_array = la_tri_arr;
edge_feat_data.v_array = la_v_arr;
@@ -2112,6 +2158,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
&edge_feat_settings);
LooseEdgeData loose_data = {0};
+
if (la_data->conf.use_loose) {
/* Only identifying floating edges at this point because other edges has been taken care of
* inside #lineart_identify_mlooptri_feature_edges function. */
@@ -2121,26 +2168,32 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
edge_loose_settings.func_reduce = loose_data_sum_reduce;
edge_loose_settings.userdata_chunk = &loose_data;
edge_loose_settings.userdata_chunk_size = sizeof(LooseEdgeData);
- loose_data.me = me;
+ loose_data.edges = BKE_mesh_edges(me);
BLI_task_parallel_range(
0, me->totedge, &loose_data, lineart_identify_loose_edges, &edge_loose_settings);
}
int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count;
- la_edge_arr = lineart_mem_acquire_thread(&la_data->render_data_pool,
+ la_edge_arr = lineart_mem_acquire_thread(la_data->edge_data_pool,
sizeof(LineartEdge) * allocate_la_e);
- la_seg_arr = lineart_mem_acquire_thread(&la_data->render_data_pool,
+ la_seg_arr = lineart_mem_acquire_thread(la_data->edge_data_pool,
sizeof(LineartEdgeSegment) * allocate_la_e);
BLI_spin_lock(&la_data->lock_task);
elem_link_node = lineart_list_append_pointer_pool_sized_thread(
&la_data->geom.line_buffer_pointers,
- &la_data->render_data_pool,
+ la_data->edge_data_pool,
la_edge_arr,
sizeof(LineartElementLinkNode));
BLI_spin_unlock(&la_data->lock_task);
elem_link_node->element_count = allocate_la_e;
elem_link_node->object_ref = orig_ob;
+ elem_link_node->obindex = ob_info->obindex;
+
+ LineartElementLinkNode *shadow_eln = NULL;
+ if (shadow_elns) {
+ shadow_eln = lineart_find_matching_eln(shadow_elns, ob_info->obindex);
+ }
/* Start of the edge/seg arr */
LineartEdge *la_edge;
@@ -2185,7 +2238,19 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
}
la_edge->flags = use_type;
la_edge->object_ref = orig_ob;
+ la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
BLI_addtail(&la_edge->segments, la_seg);
+
+ if (shadow_eln) {
+ /* TODO(Yiming): It's gonna be faster to do this operation after second stage occlusion if
+ * we only need visible segments to have shadow info, however that way we lose information
+ * on "shadow behind transparency window" type of region. */
+ LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
+ if (shadow_e) {
+ lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
+ }
+ }
+
if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
usage == OBJECT_LRT_NO_INTERSECTION) {
lineart_add_edge_to_array_thread(ob_info, la_edge);
@@ -2208,19 +2273,27 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData
if (loose_data.loose_array) {
for (int i = 0; i < loose_data.loose_count; i++) {
- la_edge->v1 = &la_v_arr[loose_data.loose_array[i]->v1];
- la_edge->v2 = &la_v_arr[loose_data.loose_array[i]->v2];
+ const MEdge *edge = &loose_data.edges[loose_data.loose_array[i]];
+ la_edge->v1 = &la_v_arr[edge->v1];
+ la_edge->v2 = &la_v_arr[edge->v2];
la_edge->flags = LRT_EDGE_FLAG_LOOSE;
la_edge->object_ref = orig_ob;
+ la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
BLI_addtail(&la_edge->segments, la_seg);
if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
usage == OBJECT_LRT_NO_INTERSECTION) {
lineart_add_edge_to_array_thread(ob_info, la_edge);
+ if (shadow_eln) {
+ LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
+ if (shadow_e) {
+ lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
+ }
+ }
}
la_edge++;
la_seg++;
}
- MEM_freeN(loose_data.loose_array);
+ MEM_SAFE_FREE(loose_data.loose_array);
}
MEM_freeN(edge_feat_data.edge_nabr);
@@ -2234,7 +2307,7 @@ static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool),
LineartObjectLoadTaskInfo *olti)
{
for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) {
- lineart_geometry_object_load(obi, olti->ld);
+ lineart_geometry_object_load(obi, olti->ld, olti->shadow_elns);
}
}
@@ -2256,6 +2329,26 @@ static uchar lineart_intersection_mask_check(Collection *c, Object *ob)
return 0;
}
+static uchar lineart_intersection_priority_check(Collection *c, Object *ob)
+{
+ if (ob->lineart.flags & OBJECT_LRT_OWN_INTERSECTION_PRIORITY) {
+ return ob->lineart.intersection_priority;
+ }
+
+ LISTBASE_FOREACH (CollectionChild *, cc, &c->children) {
+ uchar result = lineart_intersection_priority_check(cc->collection, ob);
+ if (result) {
+ return result;
+ }
+ }
+ if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
+ if (c->lineart_flags & COLLECTION_LRT_USE_INTERSECTION_PRIORITY) {
+ return c->lineart_intersection_priority;
+ }
+ }
+ return 0;
+}
+
/**
* See if this object in such collection is used for generating line art,
* Disabling a collection for line art will doable all objects inside.
@@ -2325,7 +2418,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_
use_olti->pending = obi;
}
-static bool lineart_geometry_check_visible(double (*model_view_proj)[4],
+static bool lineart_geometry_check_visible(double model_view_proj[4][4],
double shift_x,
double shift_y,
Mesh *use_mesh)
@@ -2376,17 +2469,21 @@ static void lineart_object_load_single_instance(LineartData *ld,
float use_mat[4][4],
bool is_render,
LineartObjectLoadTaskInfo *olti,
- int thread_count)
+ int thread_count,
+ int obindex)
{
LineartObjectInfo *obi = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartObjectInfo));
obi->usage = lineart_usage_check(scene->master_collection, ob, is_render);
obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, ob);
+ obi->intersection_priority = lineart_intersection_priority_check(scene->master_collection, ob);
Mesh *use_mesh;
if (obi->usage == OBJECT_LRT_EXCLUDE) {
return;
}
+ obi->obindex = obindex << LRT_OBINDEX_SHIFT;
+
/* Prepare the matrix used for transforming this specific object (instance). This has to be
* done before mesh boundbox check because the function needs that. */
mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, ld->conf.view_projection, use_mat);
@@ -2429,47 +2526,49 @@ static void lineart_object_load_single_instance(LineartData *ld,
obi->original_me = use_mesh;
obi->original_ob = (ref_ob->id.orig_id ? (Object *)ref_ob->id.orig_id : (Object *)ref_ob);
+ obi->original_ob_eval = DEG_get_evaluated_object(depsgraph, obi->original_ob);
lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly);
}
-static void lineart_main_load_geometries(
- Depsgraph *depsgraph,
- Scene *scene,
- Object *camera /* Still use camera arg for convenience. */,
- LineartData *ld,
- bool allow_duplicates)
+void lineart_main_load_geometries(Depsgraph *depsgraph,
+ Scene *scene,
+ Object *camera /* Still use camera arg for convenience. */,
+ LineartData *ld,
+ bool allow_duplicates,
+ bool do_shadow_casting,
+ ListBase *shadow_elns)
{
double proj[4][4], view[4][4], result[4][4];
float inv[4][4];
- Camera *cam = camera->data;
- float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
- int fit = BKE_camera_sensor_fit(cam->sensor_fit, ld->w, ld->h);
- double asp = ((double)ld->w / (double)ld->h);
- int bound_box_discard_count = 0;
-
- if (cam->type == CAM_PERSP) {
- if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) {
- sensor *= asp;
+ if (!do_shadow_casting) {
+ Camera *cam = camera->data;
+ float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
+ int fit = BKE_camera_sensor_fit(cam->sensor_fit, ld->w, ld->h);
+ double asp = ((double)ld->w / (double)ld->h);
+ if (cam->type == CAM_PERSP) {
+ if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) {
+ sensor *= asp;
+ }
+ if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
+ sensor /= asp;
+ }
+ const double fov = focallength_to_fov(cam->lens / (1 + ld->conf.overscan), sensor);
+ lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
}
- if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
- sensor /= asp;
+ else if (cam->type == CAM_ORTHO) {
+ const double w = cam->ortho_scale / 2;
+ lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
}
- const double fov = focallength_to_fov(cam->lens / (1 + ld->conf.overscan), sensor);
- lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
- }
- else if (cam->type == CAM_ORTHO) {
- const double w = cam->ortho_scale / 2;
- lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
- }
- invert_m4_m4(inv, ld->conf.cam_obmat);
- mul_m4db_m4db_m4fl_uniq(result, proj, inv);
- copy_m4_m4_db(proj, result);
- copy_m4_m4_db(ld->conf.view_projection, proj);
+ invert_m4_m4(inv, ld->conf.cam_obmat);
+ mul_m4db_m4db_m4fl_uniq(result, proj, inv);
+ copy_m4_m4_db(proj, result);
+ copy_m4_m4_db(ld->conf.view_projection, proj);
- unit_m4_db(view);
- copy_m4_m4_db(ld->conf.view, view);
+ unit_m4_db(view);
+ copy_m4_m4_db(ld->conf.view, view);
+ }
BLI_listbase_clear(&ld->geom.triangle_buffer_pointers);
BLI_listbase_clear(&ld->geom.vertex_buffer_pointers);
@@ -2480,6 +2579,8 @@ static void lineart_main_load_geometries(
}
int thread_count = ld->thread_count;
+ int bound_box_discard_count = 0;
+ int obindex = 0;
/* This memory is in render buffer memory pool. So we don't need to free those after loading. */
LineartObjectLoadTaskInfo *olti = lineart_mem_acquire(
@@ -2499,6 +2600,9 @@ static void lineart_main_load_geometries(
/* XXX(@Yiming): Temporary solution, this iterator is technically unsafe to use *during*
* depsgraph evaluation, see D14997 for detailed explanations. */
DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) {
+
+ obindex++;
+
Object *eval_ob = DEG_get_evaluated_object(depsgraph, ob);
if (!eval_ob) {
@@ -2512,8 +2616,16 @@ static void lineart_main_load_geometries(
}
if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) {
- lineart_object_load_single_instance(
- ld, depsgraph, scene, eval_ob, eval_ob, eval_ob->obmat, is_render, olti, thread_count);
+ lineart_object_load_single_instance(ld,
+ depsgraph,
+ scene,
+ eval_ob,
+ eval_ob,
+ eval_ob->obmat,
+ is_render,
+ olti,
+ thread_count,
+ obindex);
}
}
DEG_OBJECT_ITER_END;
@@ -2525,6 +2637,7 @@ static void lineart_main_load_geometries(
}
for (int i = 0; i < thread_count; i++) {
olti[i].ld = ld;
+ olti[i].shadow_elns = shadow_elns;
olti[i].thread_id = i;
BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL);
}
@@ -2553,6 +2666,7 @@ static void lineart_main_load_geometries(
}
LineartVert *v = (LineartVert *)obi->v_eln->pointer;
int v_count = obi->v_eln->element_count;
+ obi->v_eln->global_index_offset = global_i;
for (int vi = 0; vi < v_count; vi++) {
v[vi].index += global_i;
}
@@ -2600,14 +2714,25 @@ static bool lineart_triangle_get_other_verts(const LineartTriangle *tri,
return false;
}
-static bool lineart_edge_from_triangle(const LineartTriangle *tri,
- const LineartEdge *e,
- bool allow_overlapping_edges)
+bool lineart_edge_from_triangle(const LineartTriangle *tri,
+ const LineartEdge *e,
+ bool allow_overlapping_edges)
{
- /* Normally we just determine from the pointer address. */
- if (e->t1 == tri || e->t2 == tri) {
- return true;
+ const LineartEdge *use_e = e;
+ if (e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) {
+ if (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) ||
+ (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference)) {
+ return true;
+ }
+ }
+ else {
+ /* Normally we just determine from identifiers of adjacent triangles. */
+ if ((use_e->t1 && use_e->t1->target_reference == tri->target_reference) ||
+ (use_e->t2 && use_e->t2->target_reference == tri->target_reference)) {
+ return true;
+ }
}
+
/* If allows overlapping, then we compare the vertex coordinates one by one to determine if one
* edge is from specific triangle. This is slower but can handle edge split cases very well. */
if (allow_overlapping_edges) {
@@ -2681,14 +2806,13 @@ static bool lineart_edge_from_triangle(const LineartTriangle *tri,
* While current "edge aligned" fix isn't ideal, it does solve most of the precision issue
* especially in orthographic camera mode.
*/
-static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
- const LineartTriangle *tri,
+static bool lineart_triangle_edge_image_space_occlusion(const LineartTriangle *tri,
const LineartEdge *e,
const double *override_camera_loc,
const bool override_cam_is_persp,
const bool allow_overlapping_edges,
- const double vp[4][4],
- const double *camera_dir,
+ const double m_view_projection[4][4],
+ const double camera_dir[3],
const float cam_shift_x,
const float cam_shift_y,
double *from,
@@ -2701,7 +2825,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
int isec_e1, isec_e2, isec_e3;
/* If edge is parallel to one of the edges in the triangle. */
bool para_e1, para_e2, para_e3;
- enum LineartPointTri state_v1 = 0, state_v2 = 0;
+ enum LineartPointTri state_v1 = LRT_OUTSIDE_TRIANGLE, state_v2 = LRT_OUTSIDE_TRIANGLE;
double dir_v1[3];
double dir_v2[3];
@@ -2750,6 +2874,18 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
dot_v2 = dot_v3v3_db(dir_v2, tri->gn);
dot_f = dot_v3v3_db(dir_cam, tri->gn);
+ if ((e->flags & LRT_EDGE_FLAG_PROJECTED_SHADOW) &&
+ (e->target_reference == tri->target_reference)) {
+ if (((dot_f > 0) && (e->flags & LRT_EDGE_FLAG_SHADOW_FACING_LIGHT)) ||
+ ((dot_f < 0) && (!(e->flags & LRT_EDGE_FLAG_SHADOW_FACING_LIGHT)))) {
+ *from = 0.0f;
+ *to = 1.0f;
+ return true;
+ }
+
+ return false;
+ }
+
/* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_
* faces in perspective mode would get erroneously caught in this condition where they really
* are legit faces that would produce occlusion, but haven't encountered those yet in my test
@@ -2797,7 +2933,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
/* Transform the cut from geometry space to image space. */
if (override_cam_is_persp) {
interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut);
- mul_v4_m4v3_db(trans, vp, gloc);
+ mul_v4_m4v3_db(trans, m_view_projection, gloc);
mul_v3db_db(trans, (1 / trans[3]));
trans[0] -= cam_shift_x * 2;
trans[1] -= cam_shift_y * 2;
@@ -3038,7 +3174,7 @@ static LineartVert *lineart_triangle_share_point(const LineartTriangle *l,
}
static bool lineart_triangle_2v_intersection_math(
- LineartVert *v1, LineartVert *v2, LineartTriangle *tri, double *last, double *rv)
+ LineartVert *v1, LineartVert *v2, LineartTriangle *tri, const double *last, double *rv)
{
/* Direction vectors for the edge verts. We will check if the verts are on the same side of the
* triangle or not. */
@@ -3166,10 +3302,13 @@ static void lineart_add_isec_thread(LineartIsecThread *th,
th->array = new_array;
}
LineartIsecSingle *isec_single = &th->array[th->current];
- copy_v3fl_v3db(isec_single->v1, v1);
- copy_v3fl_v3db(isec_single->v2, v2);
+ copy_v3_v3_db(isec_single->v1, v1);
+ copy_v3_v3_db(isec_single->v2, v2);
isec_single->tri1 = tri1;
isec_single->tri2 = tri2;
+ if (tri1->target_reference > tri2->target_reference) {
+ SWAP(LineartTriangle *, isec_single->tri1, isec_single->tri2);
+ }
th->current++;
}
@@ -3303,7 +3442,7 @@ static void lineart_triangle_intersect_in_bounding_area(LineartTriangle *tri,
/**
* The calculated view vector will point towards the far-plane from the camera position.
*/
-static void lineart_main_get_view_vector(LineartData *ld)
+void lineart_main_get_view_vector(LineartData *ld)
{
float direction[3] = {0, 0, 1};
float trans[3];
@@ -3311,7 +3450,6 @@ static void lineart_main_get_view_vector(LineartData *ld)
float obmat_no_scale[4][4];
copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat);
-
normalize_v3(obmat_no_scale[0]);
normalize_v3(obmat_no_scale[1]);
normalize_v3(obmat_no_scale[2]);
@@ -3320,9 +3458,31 @@ static void lineart_main_get_view_vector(LineartData *ld)
mul_v3_mat3_m4v3(trans, inv, direction);
copy_m4_m4(ld->conf.cam_obmat, obmat_no_scale);
copy_v3db_v3fl(ld->conf.view_vector, trans);
+
+ if (ld->conf.light_reference_available) {
+ copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat_secondary);
+ normalize_v3(obmat_no_scale[0]);
+ normalize_v3(obmat_no_scale[1]);
+ normalize_v3(obmat_no_scale[2]);
+ invert_m4_m4(inv, obmat_no_scale);
+ transpose_m4(inv);
+ mul_v3_mat3_m4v3(trans, inv, direction);
+ copy_m4_m4(ld->conf.cam_obmat_secondary, obmat_no_scale);
+ copy_v3db_v3fl(ld->conf.view_vector_secondary, trans);
+ }
}
-static void lineart_destroy_render_data(LineartData *ld)
+static void lineart_end_bounding_area_recursive(LineartBoundingArea *ba)
+{
+ BLI_spin_end(&ba->lock);
+ if (ba->child) {
+ for (int i = 0; i < 4; i++) {
+ lineart_end_bounding_area_recursive(&ba->child[i]);
+ }
+ }
+}
+
+void lineart_destroy_render_data_keep_init(LineartData *ld)
{
if (ld == NULL) {
return;
@@ -3335,19 +3495,33 @@ static void lineart_destroy_render_data(LineartData *ld)
BLI_listbase_clear(&ld->geom.line_buffer_pointers);
BLI_listbase_clear(&ld->geom.triangle_buffer_pointers);
- BLI_spin_end(&ld->lock_task);
- BLI_spin_end(&ld->lock_cuts);
- BLI_spin_end(&ld->render_data_pool.lock_mem);
-
if (ld->pending_edges.array) {
MEM_freeN(ld->pending_edges.array);
}
+ for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
+ lineart_end_bounding_area_recursive(&ld->qtree.initials[i]);
+ }
lineart_free_bounding_area_memories(ld);
lineart_mem_destroy(&ld->render_data_pool);
}
+static void lineart_destroy_render_data(LineartData *ld)
+{
+ if (ld == NULL) {
+ return;
+ }
+
+ BLI_spin_end(&ld->lock_task);
+ BLI_spin_end(&ld->lock_cuts);
+ BLI_spin_end(&ld->render_data_pool.lock_mem);
+
+ lineart_destroy_render_data_keep_init(ld);
+
+ lineart_mem_destroy(&ld->render_data_pool);
+}
+
void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd)
{
LineartData *ld = lmd->la_data_ptr;
@@ -3390,7 +3564,7 @@ static LineartData *lineart_create_render_buffer(Scene *scene,
lmd->cache = lc;
lmd->la_data_ptr = ld;
- lc->rb_edge_types = lmd->edge_types_override;
+ lc->all_enabled_edge_types = lmd->edge_types_override;
if (!scene || !camera || !lc) {
return NULL;
@@ -3432,6 +3606,16 @@ static LineartData *lineart_create_render_buffer(Scene *scene,
ld->conf.shift_x /= (1 + ld->conf.overscan);
ld->conf.shift_y /= (1 + ld->conf.overscan);
+ if (lmd->light_contour_object) {
+ Object *light_obj = lmd->light_contour_object;
+ copy_v3db_v3fl(ld->conf.camera_pos_secondary, light_obj->obmat[3]);
+ copy_m4_m4(ld->conf.cam_obmat_secondary, light_obj->obmat);
+ ld->conf.light_reference_available = true;
+ if (light_obj->type == OB_LAMP) {
+ ld->conf.cam_is_persp_secondary = ((Light *)light_obj->data)->type != LA_SUN;
+ }
+ }
+
ld->conf.crease_threshold = cos(M_PI - lmd->crease_threshold);
ld->conf.chaining_image_threshold = lmd->chaining_image_threshold;
ld->conf.angle_splitting_threshold = lmd->angle_splitting_threshold;
@@ -3460,16 +3644,26 @@ static LineartData *lineart_create_render_buffer(Scene *scene,
* occlusion levels will get ignored. */
ld->conf.max_occlusion_level = lmd->level_end_override;
- ld->conf.use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0;
-
int16_t edge_types = lmd->edge_types_override;
+ /* lmd->edge_types_override contains all used flags in the modifier stack. */
ld->conf.use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0;
ld->conf.use_crease = (edge_types & LRT_EDGE_FLAG_CREASE) != 0;
ld->conf.use_material = (edge_types & LRT_EDGE_FLAG_MATERIAL) != 0;
ld->conf.use_edge_marks = (edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0;
ld->conf.use_intersections = (edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0;
ld->conf.use_loose = (edge_types & LRT_EDGE_FLAG_LOOSE) != 0;
+ ld->conf.use_light_contour = ((edge_types & LRT_EDGE_FLAG_LIGHT_CONTOUR) != 0 &&
+ (lmd->light_contour_object != NULL));
+ ld->conf.use_shadow = ((edge_types & LRT_EDGE_FLAG_PROJECTED_SHADOW) != 0 &&
+ (lmd->light_contour_object != NULL));
+
+ ld->conf.shadow_selection = lmd->shadow_selection_override;
+ ld->conf.shadow_enclose_shapes = lmd->shadow_selection_override ==
+ LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES;
+ ld->conf.shadow_use_silhouette = lmd->shadow_use_silhouette_override != 0;
+
+ ld->conf.use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0;
ld->conf.filter_face_mark_invert = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_INVERT) != 0;
ld->conf.filter_face_mark = (lmd->calculation_flags & LRT_FILTER_FACE_MARK) != 0;
@@ -3480,6 +3674,9 @@ static LineartData *lineart_create_render_buffer(Scene *scene,
ld->chain_data_pool = &lc->chain_data_pool;
+ /* See #LineartData::edge_data_pool for explanation. */
+ ld->edge_data_pool = &ld->render_data_pool;
+
BLI_spin_init(&ld->lock_task);
BLI_spin_init(&ld->lock_cuts);
BLI_spin_init(&ld->render_data_pool.lock_mem);
@@ -3494,7 +3691,7 @@ static int lineart_triangle_size_get(LineartData *ld)
return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (ld->thread_count));
}
-static void lineart_main_bounding_area_make_initial(LineartData *ld)
+void lineart_main_bounding_area_make_initial(LineartData *ld)
{
/* Initial tile split is defined as 4 (subdivided as 4*4), increasing the value allows the
* algorithm to build the acceleration structure for bigger scenes a little faster but not as
@@ -3522,9 +3719,12 @@ static void lineart_main_bounding_area_make_initial(LineartData *ld)
ld->qtree.tile_width = span_w;
ld->qtree.tile_height = span_h;
- ld->qtree.tile_count = sp_w * sp_h;
- ld->qtree.initials = lineart_mem_acquire(&ld->render_data_pool,
- sizeof(LineartBoundingArea) * ld->qtree.tile_count);
+ ld->qtree.initial_tile_count = sp_w * sp_h;
+ ld->qtree.initials = lineart_mem_acquire(
+ &ld->render_data_pool, sizeof(LineartBoundingArea) * ld->qtree.initial_tile_count);
+ for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
+ BLI_spin_init(&ld->qtree.initials[i].lock);
+ }
/* Initialize tiles. */
for (row = 0; row < sp_h; row++) {
@@ -3706,7 +3906,7 @@ static void lineart_bounding_areas_connect_recursive(LineartData *ld, LineartBou
}
}
-static void lineart_main_bounding_areas_connect_post(LineartData *ld)
+void lineart_main_bounding_areas_connect_post(LineartData *ld)
{
int total_tile_initial = ld->qtree.count_x * ld->qtree.count_y;
int tiles_per_row = ld->qtree.count_x;
@@ -3788,7 +3988,7 @@ static void lineart_bounding_area_split(LineartData *ld,
BLI_spin_init(&ba[i].lock);
}
- for (int i = 0; i < root->triangle_count; i++) {
+ for (uint32_t i = 0; i < root->triangle_count; i++) {
LineartTriangle *tri = root->linked_triangles[i];
double b[4];
@@ -3816,8 +4016,6 @@ static void lineart_bounding_area_split(LineartData *ld,
/* At this point the child tiles are fully initialized and it's safe for new triangles to be
* inserted, so assign root->child for #lineart_bounding_area_link_triangle to use. */
root->child = ba;
-
- ld->qtree.tile_count += 3;
}
static bool lineart_bounding_area_edge_intersect(LineartData *UNUSED(fb),
@@ -3829,10 +4027,8 @@ static bool lineart_bounding_area_edge_intersect(LineartData *UNUSED(fb),
double converted[4];
double c1, c;
- if (((converted[0] = (double)ba->l) > MAX2(l[0], r[0])) ||
- ((converted[1] = (double)ba->r) < MIN2(l[0], r[0])) ||
- ((converted[2] = (double)ba->b) > MAX2(l[1], r[1])) ||
- ((converted[3] = (double)ba->u) < MIN2(l[1], r[1]))) {
+ if (((converted[0] = ba->l) > MAX2(l[0], r[0])) || ((converted[1] = ba->r) < MIN2(l[0], r[0])) ||
+ ((converted[2] = ba->b) > MAX2(l[1], r[1])) || ((converted[3] = ba->u) < MIN2(l[1], r[1]))) {
return false;
}
@@ -3865,22 +4061,26 @@ static bool lineart_bounding_area_edge_intersect(LineartData *UNUSED(fb),
static bool lineart_bounding_area_triangle_intersect(LineartData *fb,
LineartTriangle *tri,
- LineartBoundingArea *ba)
+ LineartBoundingArea *ba,
+ bool *r_triangle_vert_inside)
{
double p1[2], p2[2], p3[2], p4[2];
double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord;
- p3[0] = p1[0] = (double)ba->l;
- p2[1] = p1[1] = (double)ba->b;
- p2[0] = p4[0] = (double)ba->r;
- p3[1] = p4[1] = (double)ba->u;
+ p3[0] = p1[0] = ba->l;
+ p2[1] = p1[1] = ba->b;
+ p2[0] = p4[0] = ba->r;
+ p3[1] = p4[1] = ba->u;
if ((FBC1[0] >= p1[0] && FBC1[0] <= p2[0] && FBC1[1] >= p1[1] && FBC1[1] <= p3[1]) ||
(FBC2[0] >= p1[0] && FBC2[0] <= p2[0] && FBC2[1] >= p1[1] && FBC2[1] <= p3[1]) ||
(FBC3[0] >= p1[0] && FBC3[0] <= p2[0] && FBC3[1] >= p1[1] && FBC3[1] <= p3[1])) {
+ *r_triangle_vert_inside = true;
return true;
}
+ *r_triangle_vert_inside = false;
+
if (lineart_point_inside_triangle(p1, FBC1, FBC2, FBC3) ||
lineart_point_inside_triangle(p2, FBC1, FBC2, FBC3) ||
lineart_point_inside_triangle(p3, FBC1, FBC2, FBC3) ||
@@ -3908,18 +4108,18 @@ static bool lineart_bounding_area_triangle_intersect(LineartData *fb,
* (#LineartBoundingArea) for intersection lines. When splitting the tile into 4 children and
* re-linking triangles into the child tiles, intersections are inhibited so we don't get
* duplicated intersection lines.
- *
*/
static void lineart_bounding_area_link_triangle(LineartData *ld,
LineartBoundingArea *root_ba,
LineartTriangle *tri,
- double *LRUB,
+ double l_r_u_b[4],
int recursive,
int recursive_level,
bool do_intersection,
struct LineartIsecThread *th)
{
- if (!lineart_bounding_area_triangle_intersect(ld, tri, root_ba)) {
+ bool triangle_vert_inside;
+ if (!lineart_bounding_area_triangle_intersect(ld, tri, root_ba, &triangle_vert_inside)) {
return;
}
@@ -3928,9 +4128,9 @@ static void lineart_bounding_area_link_triangle(LineartData *ld,
if (old_ba->child) {
/* If old_ba->child is not NULL, then tile splitting is fully finished, safe to directly insert
* into child tiles. */
- double *B1 = LRUB;
+ double *B1 = l_r_u_b;
double b[4];
- if (!LRUB) {
+ if (!l_r_u_b) {
b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
@@ -3956,7 +4156,12 @@ static void lineart_bounding_area_link_triangle(LineartData *ld,
if (old_ba->triangle_count < old_ba->max_triangle_count) {
const uint32_t old_tri_count = old_ba->triangle_count;
- old_ba->linked_triangles[old_ba->triangle_count++] = tri;
+ old_ba->linked_triangles[old_tri_count] = tri;
+
+ if (triangle_vert_inside) {
+ old_ba->insider_triangle_count++;
+ }
+ old_ba->triangle_count++;
/* Do intersections in place. */
if (do_intersection && ld->conf.use_intersections) {
@@ -3969,7 +4174,8 @@ static void lineart_bounding_area_link_triangle(LineartData *ld,
}
else { /* We need to wait for either splitting or array extension to be done. */
- if (recursive_level < ld->qtree.recursive_level) {
+ if (recursive_level < ld->qtree.recursive_level &&
+ old_ba->insider_triangle_count >= LRT_TILE_SPLITTING_TRIANGLE_LIMIT) {
if (!old_ba->child) {
/* old_ba->child==NULL, means we are the thread that's doing the splitting. */
lineart_bounding_area_split(ld, old_ba, recursive_level);
@@ -3989,7 +4195,7 @@ static void lineart_bounding_area_link_triangle(LineartData *ld,
/* Of course we still have our own triangle needs to be added. */
lineart_bounding_area_link_triangle(
- ld, root_ba, tri, LRUB, recursive, recursive_level, do_intersection, th);
+ ld, root_ba, tri, l_r_u_b, recursive, recursive_level, do_intersection, th);
}
}
@@ -4044,10 +4250,35 @@ static void lineart_bounding_area_link_edge(LineartData *ld,
}
}
+static void lineart_clear_linked_edges_recursive(LineartData *ld, LineartBoundingArea *root_ba)
+{
+ if (root_ba->child) {
+ for (int i = 0; i < 4; i++) {
+ lineart_clear_linked_edges_recursive(ld, &root_ba->child[i]);
+ }
+ }
+ if (root_ba->linked_lines) {
+ MEM_freeN(root_ba->linked_lines);
+ }
+ root_ba->line_count = 0;
+ root_ba->max_line_count = 128;
+ root_ba->linked_lines = MEM_callocN(sizeof(LineartEdge *) * root_ba->max_line_count,
+ "cleared lineart edges");
+}
+void lineart_main_clear_linked_edges(LineartData *ld)
+{
+ LineartBoundingArea *ba = ld->qtree.initials;
+ for (int i = 0; i < ld->qtree.count_y; i++) {
+ for (int j = 0; j < ld->qtree.count_x; j++) {
+ lineart_clear_linked_edges_recursive(ld, &ba[i * ld->qtree.count_x + j]);
+ }
+ }
+}
+
/**
* Link lines to their respective bounding areas.
*/
-static void lineart_main_link_lines(LineartData *ld)
+void lineart_main_link_lines(LineartData *ld)
{
LRT_ITER_ALL_LINES_BEGIN
{
@@ -4064,6 +4295,62 @@ static void lineart_main_link_lines(LineartData *ld)
LRT_ITER_ALL_LINES_END
}
+static void lineart_main_remove_unused_lines_recursive(LineartBoundingArea *ba,
+ uint8_t max_occlusion)
+{
+ if (ba->child) {
+ for (int i = 0; i < 4; i++) {
+ lineart_main_remove_unused_lines_recursive(&ba->child[i], max_occlusion);
+ }
+ return;
+ }
+
+ if (!ba->line_count) {
+ return;
+ }
+
+ int usable_count = 0;
+ for (int i = 0; i < ba->line_count; i++) {
+ LineartEdge *e = ba->linked_lines[i];
+ if (e->min_occ > max_occlusion) {
+ continue;
+ }
+ usable_count++;
+ }
+
+ if (!usable_count) {
+ ba->line_count = 0;
+ return;
+ }
+
+ LineartEdge **new_array = MEM_callocN(sizeof(LineartEdge *) * usable_count,
+ "cleaned lineart edge array");
+
+ int new_i = 0;
+ for (int i = 0; i < ba->line_count; i++) {
+ LineartEdge *e = ba->linked_lines[i];
+ if (e->min_occ > max_occlusion) {
+ continue;
+ }
+ new_array[new_i] = e;
+ new_i++;
+ }
+
+ MEM_freeN(ba->linked_lines);
+ ba->linked_lines = new_array;
+ ba->max_line_count = ba->line_count = usable_count;
+}
+
+static void lineart_main_remove_unused_lines_from_tiles(LineartData *ld)
+{
+ for (int row = 0; row < ld->qtree.count_y; row++) {
+ for (int col = 0; col < ld->qtree.count_x; col++) {
+ lineart_main_remove_unused_lines_recursive(
+ &ld->qtree.initials[row * ld->qtree.count_x + col], ld->conf.max_occlusion_level);
+ }
+ }
+}
+
static bool lineart_get_triangle_bounding_areas(
LineartData *ld, LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, int *colend)
{
@@ -4270,6 +4557,7 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d)
LineartData *ld = d->ld;
double ZMax = ld->conf.far_clip;
double ZMin = ld->conf.near_clip;
+ int total_lines = 0;
for (int i = 0; i < d->thread_count; i++) {
LineartIsecThread *th = &d->threads[i];
@@ -4279,25 +4567,39 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d)
if (!th->current) {
continue;
}
- /* We don't care about removing duplicated vert in this method, chaining can handle that,
- * and it saves us from using locks and look up tables. */
- LineartVertIntersection *v = lineart_mem_acquire(
- &ld->render_data_pool, sizeof(LineartVertIntersection) * th->current * 2);
- LineartEdge *e = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdge) * th->current);
- LineartEdgeSegment *es = lineart_mem_acquire(&ld->render_data_pool,
- sizeof(LineartEdgeSegment) * th->current);
+ total_lines += th->current;
+ }
+
+ if (!total_lines) {
+ return;
+ }
+
+ /* We don't care about removing duplicated vert in this method, chaining can handle that,
+ * and it saves us from using locks and look up tables. */
+ LineartVert *v = lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartVert) * total_lines * 2);
+ LineartEdge *e = lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdge) * total_lines);
+ LineartEdgeSegment *es = lineart_mem_acquire(ld->edge_data_pool,
+ sizeof(LineartEdgeSegment) * total_lines);
+
+ LineartElementLinkNode *eln = lineart_mem_acquire(ld->edge_data_pool,
+ sizeof(LineartElementLinkNode));
+ eln->element_count = total_lines;
+ eln->pointer = e;
+ eln->flags |= LRT_ELEMENT_INTERSECTION_DATA;
+ BLI_addhead(&ld->geom.line_buffer_pointers, eln);
+
+ for (int i = 0; i < d->thread_count; i++) {
+ LineartIsecThread *th = &d->threads[i];
+ if (!th->current) {
+ continue;
+ }
+
for (int j = 0; j < th->current; j++) {
- LineartVertIntersection *v1i = v;
- LineartVertIntersection *v2i = v + 1;
LineartIsecSingle *is = &th->array[j];
- v1i->intersecting_with = is->tri1;
- v2i->intersecting_with = is->tri2;
- LineartVert *v1 = (LineartVert *)v1i;
- LineartVert *v2 = (LineartVert *)v2i;
- v1->flag |= LRT_VERT_HAS_INTERSECTION_DATA;
- v2->flag |= LRT_VERT_HAS_INTERSECTION_DATA;
- copy_v3db_v3fl(v1->gloc, is->v1);
- copy_v3db_v3fl(v2->gloc, is->v2);
+ LineartVert *v1 = v;
+ LineartVert *v2 = v + 1;
+ copy_v3_v3_db(v1->gloc, is->v1);
+ copy_v3_v3_db(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, ld->conf.view_projection, v1->gloc);
@@ -4320,10 +4622,34 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d)
e->v2 = v2;
e->t1 = is->tri1;
e->t2 = is->tri2;
+ /* This is so we can also match intersection edges from shadow to later viewing stage. */
+ e->edge_identifier = (((uint64_t)e->t1->target_reference) << 32) | e->t2->target_reference;
e->flags = LRT_EDGE_FLAG_INTERSECTION;
e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask);
BLI_addtail(&e->segments, es);
+ int obi1 = (e->t1->target_reference & LRT_OBINDEX_HIGHER);
+ int obi2 = (e->t2->target_reference & LRT_OBINDEX_HIGHER);
+ LineartElementLinkNode *eln1 = lineart_find_matching_eln(&ld->geom.line_buffer_pointers,
+ obi1);
+ LineartElementLinkNode *eln2 = obi1 == obi2 ? eln1 :
+ lineart_find_matching_eln(
+ &ld->geom.line_buffer_pointers, obi2);
+ Object *ob1 = eln1 ? eln1->object_ref : NULL;
+ Object *ob2 = eln2 ? eln2->object_ref : NULL;
+ if (e->t1->intersection_priority > e->t2->intersection_priority) {
+ e->object_ref = ob1;
+ }
+ else if (e->t1->intersection_priority < e->t2->intersection_priority) {
+ e->object_ref = ob2;
+ }
+ else { /* equal priority */
+ if (ob1 == ob2) {
+ /* object_ref should be ambiguous if intersection lines comes from different objects. */
+ e->object_ref = ob1;
+ }
+ }
+
lineart_add_edge_to_array(&ld->pending_edges, e);
v += 2;
@@ -4337,7 +4663,7 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d)
* Sequentially add triangles into render buffer, intersection lines between those triangles will
* also be computed at the same time.
*/
-static void lineart_main_add_triangles(LineartData *ld)
+void lineart_main_add_triangles(LineartData *ld)
{
double t_start;
if (G.debug_value == 4000) {
@@ -4345,7 +4671,7 @@ static void lineart_main_add_triangles(LineartData *ld)
}
/* Initialize per-thread data for thread task scheduling information and storing intersection
- * results. */
+ * results. */
LineartIsecData d = {0};
lineart_init_isec_thread(&d, ld, ld->thread_count);
@@ -4356,8 +4682,9 @@ static void lineart_main_add_triangles(LineartData *ld)
BLI_task_pool_work_and_wait(tp);
BLI_task_pool_free(tp);
- /* Create actual lineart edges from intersection results. */
- lineart_create_edges_from_isec_data(&d);
+ if (ld->conf.use_intersections) {
+ lineart_create_edges_from_isec_data(&d);
+ }
lineart_destroy_isec_thread(&d);
@@ -4371,9 +4698,11 @@ static void lineart_main_add_triangles(LineartData *ld)
* This function gets the tile for the point `e->v1`, and later use #lineart_bounding_area_next()
* to get next along the way.
*/
-static LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld, LineartEdge *e)
+LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld,
+ double *fbcoord1,
+ double *fbcoord2)
{
- double data[2] = {e->v1->fbcoord[0], e->v1->fbcoord[1]};
+ double data[2] = {fbcoord1[0], fbcoord1[1]};
double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1};
double r = 1, sr = 1;
bool p_unused;
@@ -4382,23 +4711,19 @@ static LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld, Li
return lineart_get_bounding_area(ld, data[0], data[1]);
}
- if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, LU, RU, &sr, &p_unused) &&
- sr < r && sr > 0) {
+ if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LU, RU, &sr, &p_unused) && sr < r && sr > 0) {
r = sr;
}
- if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, LB, RB, &sr, &p_unused) &&
- sr < r && sr > 0) {
+ if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, RB, &sr, &p_unused) && sr < r && sr > 0) {
r = sr;
}
- if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, LB, LU, &sr, &p_unused) &&
- sr < r && sr > 0) {
+ if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, LU, &sr, &p_unused) && sr < r && sr > 0) {
r = sr;
}
- if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, RB, RU, &sr, &p_unused) &&
- sr < r && sr > 0) {
+ if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, RB, RU, &sr, &p_unused) && sr < r && sr > 0) {
r = sr;
}
- interp_v2_v2v2_db(data, e->v1->fbcoord, e->v2->fbcoord, r);
+ interp_v2_v2v2_db(data, fbcoord1, fbcoord2, r);
return lineart_get_bounding_area(ld, data[0], data[1]);
}
@@ -4407,15 +4732,16 @@ static LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld, Li
* This march along one render line in image space and
* get the next bounding area the line is crossing.
*/
-static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this,
- LineartEdge *e,
- double x,
- double y,
- double k,
- int positive_x,
- int positive_y,
- double *next_x,
- double *next_y)
+LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this,
+ double *fbcoord1,
+ double *fbcoord2,
+ double x,
+ double y,
+ double k,
+ int positive_x,
+ int positive_y,
+ double *next_x,
+ double *next_y)
{
double rx, ry, ux, uy, lx, ly, bx, by;
double r1, r2;
@@ -4430,8 +4756,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
if (positive_y > 0) {
uy = this->u;
ux = x + (uy - y) / k;
- r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], rx);
- r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], ux);
+ r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
+ r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
if (MIN2(r1, r2) > 1) {
return 0;
}
@@ -4463,8 +4789,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
else if (positive_y < 0) {
by = this->b;
bx = x + (by - y) / k;
- r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], rx);
- r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], bx);
+ r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
+ r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
if (MIN2(r1, r2) > 1) {
return 0;
}
@@ -4491,7 +4817,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
}
/* If the line is completely horizontal, in which Y difference == 0. */
else {
- r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], this->r);
+ r1 = ratiod(fbcoord1[0], fbcoord2[0], this->r);
if (r1 > 1) {
return 0;
}
@@ -4515,8 +4841,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
if (positive_y > 0) {
uy = this->u;
ux = x + (uy - y) / k;
- r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], lx);
- r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], ux);
+ r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
+ r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
if (MIN2(r1, r2) > 1) {
return 0;
}
@@ -4546,8 +4872,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
else if (positive_y < 0) {
by = this->b;
bx = x + (by - y) / k;
- r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], lx);
- r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], bx);
+ r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
+ r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
if (MIN2(r1, r2) > 1) {
return 0;
}
@@ -4574,7 +4900,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
}
/* Again, horizontal. */
else {
- r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], this->l);
+ r1 = ratiod(fbcoord1[0], fbcoord2[0], this->l);
if (r1 > 1) {
return 0;
}
@@ -4591,7 +4917,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
/* If the line is completely vertical, hence X difference == 0. */
else {
if (positive_y > 0) {
- r1 = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], this->u);
+ r1 = ratiod(fbcoord1[1], fbcoord2[1], this->u);
if (r1 > 1) {
return 0;
}
@@ -4605,7 +4931,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
}
}
else if (positive_y < 0) {
- r1 = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], this->b);
+ r1 = ratiod(fbcoord1[1], fbcoord2[1], this->b);
if (r1 > 1) {
return 0;
}
@@ -4626,6 +4952,11 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
return 0;
}
+/**
+ * This is the entry point of all line art calculations.
+ *
+ * \return True when a change is made.
+ */
bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
LineartGpencilModifierData *lmd,
LineartCache **cached_result,
@@ -4637,7 +4968,6 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
Object *use_camera;
double t_start;
-
if (G.debug_value == 4000) {
t_start = PIL_check_seconds_timer();
}
@@ -4668,10 +4998,33 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
* See definition of LineartTriangleThread for details. */
ld->sizeof_triangle = lineart_triangle_size_get(ld);
+ LineartData *shadow_rb = NULL;
+ LineartElementLinkNode *shadow_veln, *shadow_eeln;
+ ListBase *shadow_elns = ld->conf.shadow_selection ? &lc->shadow_elns : NULL;
+ bool shadow_generated = lineart_main_try_generate_shadow(depsgraph,
+ scene,
+ ld,
+ lmd,
+ &lc->shadow_data_pool,
+ &shadow_veln,
+ &shadow_eeln,
+ shadow_elns,
+ &shadow_rb);
+
/* Get view vector before loading geometries, because we detect feature lines there. */
lineart_main_get_view_vector(ld);
- lineart_main_load_geometries(
- depsgraph, scene, use_camera, ld, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS);
+
+ lineart_main_load_geometries(depsgraph,
+ scene,
+ use_camera,
+ ld,
+ lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS,
+ false,
+ shadow_elns);
+
+ if (shadow_generated) {
+ lineart_main_transform_and_add_shadow(ld, shadow_veln, shadow_eeln);
+ }
if (!ld->geom.vertex_buffer_pointers.first) {
/* No geometry loaded, return early. */
@@ -4701,6 +5054,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
* can do its job. */
lineart_main_add_triangles(ld);
+ /* Add shadow cuts to intersection lines as well. */
+ lineart_register_intersection_shadow_cuts(ld, shadow_elns);
+
/* Re-link bounding areas because they have been subdivided by worker threads and we need
* adjacent info. */
lineart_main_bounding_areas_connect_post(ld);
@@ -4719,6 +5075,10 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
/* Occlusion is work-and-wait. This call will not return before work is completed. */
lineart_main_occlusion_begin(ld);
+ lineart_main_make_enclosed_shapes(ld, shadow_rb);
+
+ lineart_main_remove_unused_lines_from_tiles(ld);
+
/* Chaining is all single threaded. See lineart_chain.c
* In this particular call, only lines that are geometrically connected (share the _exact_
* same end point) will be chained together. */
@@ -4732,10 +5092,6 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
* the place threshold value gets involved. */
MOD_lineart_chain_connect(ld);
- float *t_image = &lmd->chaining_image_threshold;
- /* This configuration ensures there won't be accidental lost of short unchained segments. */
- MOD_lineart_chain_discard_short(ld, MIN2(*t_image, 0.001f) - FLT_EPSILON);
-
if (ld->conf.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
@@ -4756,11 +5112,24 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
ld, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA);
}
+ if (ld->conf.shadow_use_silhouette) {
+ MOD_lineart_chain_find_silhouette_backdrop_objects(ld);
+ }
+
/* Finally transfer the result list into cache. */
memcpy(&lc->chains, &ld->chains, sizeof(ListBase));
/* At last, we need to clear flags so we don't confuse GPencil generation calls. */
MOD_lineart_chain_clear_picked_flag(lc);
+
+ MOD_lineart_finalize_chains(ld);
+ }
+
+ lineart_mem_destroy(&lc->shadow_data_pool);
+
+ if (ld->conf.shadow_enclose_shapes && shadow_rb) {
+ lineart_destroy_render_data_keep_init(shadow_rb);
+ MEM_freeN(shadow_rb);
}
if (G.debug_value == 4000) {
@@ -4773,18 +5142,6 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
return true;
}
-static int UNUSED_FUNCTION(lineart_rb_edge_types)(LineartData *ld)
-{
- int types = 0;
- types |= ld->conf.use_contour ? LRT_EDGE_FLAG_CONTOUR : 0;
- types |= ld->conf.use_crease ? LRT_EDGE_FLAG_CREASE : 0;
- types |= ld->conf.use_material ? LRT_EDGE_FLAG_MATERIAL : 0;
- types |= ld->conf.use_edge_marks ? LRT_EDGE_FLAG_EDGE_MARK : 0;
- types |= ld->conf.use_intersections ? LRT_EDGE_FLAG_INTERSECTION : 0;
- types |= ld->conf.use_loose ? LRT_EDGE_FLAG_LOOSE : 0;
- return types;
-}
-
static void lineart_gpencil_generate(LineartCache *cache,
Depsgraph *depsgraph,
Object *gpencil_object,
@@ -4802,6 +5159,8 @@ static void lineart_gpencil_generate(LineartCache *cache,
uchar intersection_mask,
int16_t thickness,
float opacity,
+ uchar shaodow_selection,
+ uchar silhouette_mode,
const char *source_vgname,
const char *vgname,
int modifier_flags)
@@ -4829,9 +5188,10 @@ static void lineart_gpencil_generate(LineartCache *cache,
/* (!orig_col && !orig_ob) means the whole scene is selected. */
- int enabled_types = cache->rb_edge_types;
+ int enabled_types = cache->all_enabled_edge_types;
bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP;
bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP;
+ bool inverse_silhouette = modifier_flags & LRT_GPENCIL_INVERT_SILHOUETTE_FILTER;
LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) {
@@ -4883,11 +5243,64 @@ static void lineart_gpencil_generate(LineartCache *cache,
}
}
}
+ if (shaodow_selection) {
+ if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) {
+ /* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */
+ if ((shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED &&
+ (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)))) {
+ continue;
+ }
+ if ((shaodow_selection == LRT_SHADOW_FILTER_SHADED &&
+ (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)))) {
+ continue;
+ }
+ if (shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) {
+ uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS;
+ if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) &&
+ (test_bits != (LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_ILLUMINATED_SHAPE))) {
+ continue;
+ }
+ }
+ }
+ }
+ if (silhouette_mode && (ec->type & (LRT_EDGE_FLAG_CONTOUR))) {
+ bool is_silhouette = false;
+ if (orig_col) {
+ if (!ec->silhouette_backdrop) {
+ is_silhouette = true;
+ }
+ else if (!BKE_collection_has_object_recursive_instanced(orig_col,
+ ec->silhouette_backdrop)) {
+ is_silhouette = true;
+ }
+ }
+ else {
+ if ((!orig_ob) && (!ec->silhouette_backdrop)) {
+ is_silhouette = true;
+ }
+ }
+
+ if ((silhouette_mode == LRT_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) &&
+ ec->silhouette_backdrop != ec->object_ref) {
+ is_silhouette = true;
+ }
+
+ if (inverse_silhouette) {
+ is_silhouette = !is_silhouette;
+ }
+ if (!is_silhouette) {
+ continue;
+ }
+ }
/* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
// ec->picked = 1;
const int count = MOD_lineart_chain_count(ec);
+ if (count < 2) {
+ continue;
+ }
+
bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false);
int i;
@@ -4908,7 +5321,8 @@ static void lineart_gpencil_generate(LineartCache *cache,
if (eval_ob && eval_ob->type == OB_MESH) {
int dindex = 0;
Mesh *me = BKE_object_get_evaluated_mesh(eval_ob);
- if (me->dvert) {
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(me);
+ if (dvert) {
LISTBASE_FOREACH (bDeformGroup *, db, &me->vertex_group_names) {
if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) {
if (match_output) {
@@ -4923,7 +5337,7 @@ static void lineart_gpencil_generate(LineartCache *cache,
if (vindex >= me->totvert) {
break;
}
- MDeformWeight *mdw = BKE_defvert_ensure_index(&me->dvert[vindex], dindex);
+ MDeformWeight *mdw = BKE_defvert_ensure_index(&dvert[vindex], dindex);
MDeformWeight *gdw = BKE_defvert_ensure_index(&gps->dvert[sindex], gpdg);
float use_weight = mdw->weight;
@@ -4970,6 +5384,8 @@ void MOD_lineart_gpencil_generate(LineartCache *cache,
uchar intersection_mask,
int16_t thickness,
float opacity,
+ uchar shadow_selection,
+ uchar silhouette_mode,
const char *source_vgname,
const char *vgname,
int modifier_flags)
@@ -4981,26 +5397,20 @@ void MOD_lineart_gpencil_generate(LineartCache *cache,
Object *source_object = NULL;
Collection *source_collection = NULL;
- int16_t use_types = 0;
+ int16_t 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,
@@ -5020,6 +5430,8 @@ void MOD_lineart_gpencil_generate(LineartCache *cache,
intersection_mask,
thickness,
opacity,
+ shadow_selection,
+ silhouette_mode,
source_vgname,
vgname,
modifier_flags);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 5e24061bd78..947586aaec4 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -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,95 @@ 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);
+LineartElementLinkNode *lineart_find_matching_eln_obj(struct ListBase *elns, struct Object *ob);
+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
}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
index bbf88985e6a..138c016e2e2 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
@@ -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);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c
new file mode 100644
index 00000000000..257184bae1e
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c
@@ -0,0 +1,1419 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include "MOD_gpencil_lineart.h"
+#include "MOD_lineart.h"
+
+#include "lineart_intern.h"
+
+#include "BKE_global.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_lib_id.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
+#include "BKE_scene.h"
+#include "DEG_depsgraph_query.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"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+#include "MEM_guardedalloc.h"
+
+#include "BLI_task.h"
+#include "PIL_time.h"
+
+/* Shadow loading etc. ================== */
+
+LineartElementLinkNode *lineart_find_matching_eln(ListBase *shadow_elns, int obindex)
+{
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, shadow_elns) {
+ if (eln->obindex == obindex) {
+ return eln;
+ }
+ }
+ return NULL;
+}
+
+LineartEdge *lineart_find_matching_edge(LineartElementLinkNode *shadow_eln,
+ uint64_t edge_identifier)
+{
+ LineartEdge *elist = (LineartEdge *)shadow_eln->pointer;
+ for (int i = 0; i < shadow_eln->element_count; i++) {
+ if (elist[i].edge_identifier == edge_identifier) {
+ return &elist[i];
+ }
+ }
+ return NULL;
+}
+
+static bool lineart_contour_viewed_from_dark_side(LineartData *ld, LineartEdge *e)
+{
+
+ if (!(e->flags & (LRT_EDGE_FLAG_CONTOUR | LRT_EDGE_FLAG_CONTOUR_SECONDARY))) {
+ return false;
+ }
+ double view_vector[3];
+ double light_vector[3];
+ bool side_1_facing_light = false;
+ bool side_2_facing_light = false;
+ bool side_1_facing_camera = false;
+ if (ld->conf.cam_is_persp_secondary) {
+ sub_v3_v3v3_db(light_vector, ld->conf.camera_pos_secondary, e->v1->gloc);
+ }
+ else {
+ copy_v3_v3_db(light_vector, ld->conf.view_vector_secondary);
+ }
+ double dot_light_1 = dot_v3v3_db(light_vector, e->t1->gn);
+ side_1_facing_light = (dot_light_1 > 0);
+ if (e->t2) {
+ double dot_light_2 = dot_v3v3_db(light_vector, e->t2->gn);
+ side_2_facing_light = (dot_light_2 > 0);
+ }
+ else {
+ side_2_facing_light = !side_1_facing_light;
+ }
+
+ if (ld->conf.cam_is_persp) {
+ sub_v3_v3v3_db(view_vector, ld->conf.camera_pos, e->v1->gloc);
+ }
+ else {
+ copy_v3_v3_db(view_vector, ld->conf.view_vector);
+ }
+ double dot_view_1 = dot_v3v3_db(view_vector, e->t1->gn);
+ side_1_facing_camera = (dot_view_1 > 0);
+
+ if ((side_1_facing_camera && (!side_1_facing_light) && side_2_facing_light) ||
+ ((!side_1_facing_camera) && side_1_facing_light && (!side_2_facing_light))) {
+ return true;
+ }
+ return false;
+}
+
+/* Cuts the original edge based on the occlusion results under light-camera, if segment
+ * is occluded in light-camera, then that segment on the original edge must be shaded. */
+void lineart_register_shadow_cuts(LineartData *ld, LineartEdge *e, LineartEdge *shadow_edge)
+{
+ LISTBASE_FOREACH (LineartEdgeSegment *, es, &shadow_edge->segments) {
+ /* Convert to view space cutting points. */
+ double la1 = es->ratio;
+ double la2 = es->next ? es->next->ratio : 1.0f;
+ la1 = la1 * e->v2->fbcoord[3] /
+ (e->v1->fbcoord[3] - la1 * (e->v1->fbcoord[3] - e->v2->fbcoord[3]));
+ la2 = la2 * e->v2->fbcoord[3] /
+ (e->v1->fbcoord[3] - la2 * (e->v1->fbcoord[3] - e->v2->fbcoord[3]));
+ unsigned char shadow_bits = (es->occlusion != 0) ? LRT_SHADOW_MASK_SHADED :
+ LRT_SHADOW_MASK_ILLUMINATED;
+
+ if (lineart_contour_viewed_from_dark_side(ld, e) &&
+ shadow_bits == LRT_SHADOW_MASK_ILLUMINATED) {
+ shadow_bits = LRT_SHADOW_MASK_SHADED;
+ }
+
+ lineart_edge_cut(ld, e, la1, la2, 0, 0, shadow_bits);
+ }
+}
+
+void lineart_register_intersection_shadow_cuts(LineartData *ld, ListBase *shadow_elns)
+{
+ if (!shadow_elns) {
+ return;
+ }
+
+ LineartElementLinkNode *eln_isect_shadow = NULL;
+ LineartElementLinkNode *eln_isect_original = NULL;
+
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, shadow_elns) {
+ if (eln->flags & LRT_ELEMENT_INTERSECTION_DATA) {
+ eln_isect_shadow = eln;
+ break;
+ }
+ }
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.line_buffer_pointers) {
+ if (eln->flags & LRT_ELEMENT_INTERSECTION_DATA) {
+ eln_isect_original = eln;
+ break;
+ }
+ }
+ if (!eln_isect_shadow || !eln_isect_original) {
+ return;
+ }
+
+ /* Keeping it single threaded for now because a simple parallel_for could end up getting the same
+ * #shadow_e in different threads. */
+ for (int i = 0; i < eln_isect_original->element_count; i++) {
+ LineartEdge *e = &((LineartEdge *)eln_isect_original->pointer)[i];
+ LineartEdge *shadow_e = lineart_find_matching_edge(eln_isect_shadow,
+ (uint64_t)e->edge_identifier);
+ if (shadow_e) {
+ lineart_register_shadow_cuts(ld, e, shadow_e);
+ }
+ }
+}
+
+/* Shadow computation part ================== */
+
+static LineartShadowSegment *lineart_give_shadow_segment(LineartData *ld)
+{
+ BLI_spin_lock(&ld->lock_cuts);
+
+ /* See if there is any already allocated memory we can reuse. */
+ if (ld->wasted_shadow_cuts.first) {
+ LineartShadowSegment *es = (LineartShadowSegment *)BLI_pophead(&ld->wasted_shadow_cuts);
+ BLI_spin_unlock(&ld->lock_cuts);
+ memset(es, 0, sizeof(LineartShadowSegment));
+ return (LineartShadowSegment *)es;
+ }
+ BLI_spin_unlock(&ld->lock_cuts);
+
+ /* Otherwise allocate some new memory. */
+ return (LineartShadowSegment *)lineart_mem_acquire_thread(&ld->render_data_pool,
+ sizeof(LineartShadowSegment));
+}
+
+static void lineart_shadow_segment_slice_get(double *fb_co_1,
+ double *fb_co_2,
+ double *gloc_1,
+ double *gloc_2,
+ double ratio,
+ double at_1,
+ double at_2,
+ double *r_fb_co,
+ double *r_gloc)
+{
+ double real_at = ((at_2 - at_1) == 0) ? 0 : ((ratio - at_1) / (at_2 - at_1));
+ double ga = fb_co_1[3] * real_at / (fb_co_2[3] * (1.0f - real_at) + fb_co_1[3] * real_at);
+ interp_v3_v3v3_db(r_fb_co, fb_co_1, fb_co_2, real_at);
+ r_fb_co[3] = interpd(fb_co_2[3], fb_co_1[3], ga);
+ interp_v3_v3v3_db(r_gloc, gloc_1, gloc_2, ga);
+}
+
+/**
+ * This function tries to get the closest projected segments along two end points.
+ * The x,y of s1, s2 are aligned in frame-buffer coordinates, only z,w are different.
+ * We will get the closest z/w as well as the corresponding global coordinates.
+ *
+ * \code{.unparsed}
+ * (far side)
+ * l-------r [s1] ^
+ * _-r [s2] | In this situation it will essentially return the coordinates of s2.
+ * _-` |
+ * l-` |
+ *
+ * (far side)
+ * _-r [s2] ^
+ * _-` | In this case the return coordinates would be `s2l` and `s1r`,
+ * l-----_c`-----r [s1] | and `r_new` will be assigned coordinates of `c`.
+ * _-` |
+ * l-` |
+ * \endcode
+ *
+ * Returns true when a new cut (`c`) is needed in the middle, otherwise returns false, and
+ * `*r_new_xxx` are not touched.
+ */
+static bool lineart_do_closest_segment(bool is_persp,
+ double *s1_fb_co_1,
+ double *s1_fb_co_2,
+ double *s2_fb_co_1,
+ double *s2_fb_co_2,
+ double *s1_gloc_1,
+ double *s1_gloc_2,
+ double *s2_gloc_1,
+ double *s2_gloc_2,
+ double *r_fb_co_1,
+ double *r_fb_co_2,
+ double *r_gloc_1,
+ double *r_gloc_2,
+ double *r_new_in_the_middle,
+ double *r_new_in_the_middle_global,
+ double *r_new_at,
+ bool *is_side_2r,
+ bool *use_new_ref)
+{
+ int side = 0;
+ int z_index = is_persp ? 3 : 2;
+ /* Always use the closest point to the light camera. */
+ if (s1_fb_co_1[z_index] >= s2_fb_co_1[z_index]) {
+ copy_v4_v4_db(r_fb_co_1, s2_fb_co_1);
+ copy_v3_v3_db(r_gloc_1, s2_gloc_1);
+ side++;
+ }
+ if (s1_fb_co_2[z_index] >= s2_fb_co_2[z_index]) {
+ copy_v4_v4_db(r_fb_co_2, s2_fb_co_2);
+ copy_v3_v3_db(r_gloc_2, s2_gloc_2);
+ *is_side_2r = true;
+ side++;
+ }
+ if (s1_fb_co_1[z_index] <= s2_fb_co_1[z_index]) {
+ copy_v4_v4_db(r_fb_co_1, s1_fb_co_1);
+ copy_v3_v3_db(r_gloc_1, s1_gloc_1);
+ side--;
+ }
+ if (s1_fb_co_2[z_index] <= s2_fb_co_2[z_index]) {
+ copy_v4_v4_db(r_fb_co_2, s1_fb_co_2);
+ copy_v3_v3_db(r_gloc_2, s1_gloc_2);
+ *is_side_2r = false;
+ side--;
+ }
+
+ /* No need to cut in the middle, because one segment completely overlaps the other. */
+ if (side) {
+ if (side > 0) {
+ *is_side_2r = true;
+ *use_new_ref = true;
+ }
+ else if (side < 0) {
+ *is_side_2r = false;
+ *use_new_ref = false;
+ }
+ return false;
+ }
+
+ /* Else there must be an intersection point in the middle. Use "w" value to linearly plot the
+ * position and get image space "ratio" position. */
+ double dl = s1_fb_co_1[z_index] - s2_fb_co_1[z_index];
+ double dr = s1_fb_co_2[z_index] - s2_fb_co_2[z_index];
+ double ga = ratiod(dl, dr, 0);
+ *r_new_at = is_persp ? s2_fb_co_2[3] * ga / (s2_fb_co_1[3] * (1.0f - ga) + s2_fb_co_2[3] * ga) :
+ ga;
+ interp_v3_v3v3_db(r_new_in_the_middle, s2_fb_co_1, s2_fb_co_2, *r_new_at);
+ r_new_in_the_middle[3] = interpd(s2_fb_co_2[3], s2_fb_co_1[3], ga);
+ interp_v3_v3v3_db(r_new_in_the_middle_global, s1_gloc_1, s1_gloc_2, ga);
+ *use_new_ref = true;
+
+ return true;
+}
+
+/* For each visible [segment] of the edge, create 1 shadow edge. Note if the original edge has
+ * multiple visible cuts, multiple shadow edges should be generated. */
+static void lineart_shadow_create_shadow_edge_array(LineartData *ld,
+ bool transform_edge_cuts,
+ bool do_light_contour)
+{
+ /* If the segment is short enough, we ignore them because it's not prominently visible anyway. */
+#define DISCARD_NONSENSE_SEGMENTS \
+ if (es->occlusion != 0 || \
+ (es->next && \
+ LRT_DOUBLE_CLOSE_ENOUGH(es->ratio, ((LineartEdgeSegment *)es->next)->ratio))) { \
+ LRT_ITER_ALL_LINES_NEXT; \
+ continue; \
+ }
+
+ /* Count and allocate at once to save time. */
+ int segment_count = 0;
+ uint16_t accept_types = (LRT_EDGE_FLAG_CONTOUR | LRT_EDGE_FLAG_LOOSE);
+ if (do_light_contour) {
+ accept_types |= LRT_EDGE_FLAG_LIGHT_CONTOUR;
+ }
+ LRT_ITER_ALL_LINES_BEGIN
+ {
+ /* Only contour and loose edges can actually cast shadows. We allow light contour here because
+ * we want to see if it also doubles as a view contour, in that case we also need to project
+ * them. */
+ if (!(e->flags & accept_types)) {
+ continue;
+ }
+ if (e->flags == LRT_EDGE_FLAG_LIGHT_CONTOUR) {
+ /* Check if the light contour also doubles as a view contour. */
+ LineartEdge *orig_e = (LineartEdge *)e->t1;
+ if (!orig_e->t2) {
+ e->flags |= LRT_EDGE_FLAG_CONTOUR;
+ }
+ else {
+ double vv[3];
+ double *view_vector = vv;
+ double dot_1 = 0, dot_2 = 0;
+ double result;
+ if (ld->conf.cam_is_persp) {
+ sub_v3_v3v3_db(view_vector, orig_e->v1->gloc, ld->conf.camera_pos);
+ }
+ else {
+ view_vector = ld->conf.view_vector;
+ }
+
+ dot_1 = dot_v3v3_db(view_vector, orig_e->t1->gn);
+ dot_2 = dot_v3v3_db(view_vector, orig_e->t2->gn);
+
+ if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
+ /* If this edge is both a light contour and a view contour, mark it for the convenience
+ * of generating it in the next iteration. */
+ e->flags |= LRT_EDGE_FLAG_CONTOUR;
+ }
+ }
+ if (!(e->flags & LRT_EDGE_FLAG_CONTOUR)) {
+ continue;
+ }
+ }
+ LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) {
+ DISCARD_NONSENSE_SEGMENTS
+ segment_count++;
+ }
+ }
+ LRT_ITER_ALL_LINES_END
+
+ LineartShadowEdge *sedge = lineart_mem_acquire(&ld->render_data_pool,
+ sizeof(LineartShadowEdge) * segment_count);
+ LineartShadowSegment *sseg = lineart_mem_acquire(
+ &ld->render_data_pool, sizeof(LineartShadowSegment) * segment_count * 2);
+
+ ld->shadow_edges = sedge;
+ ld->shadow_edges_count = segment_count;
+
+ int i = 0;
+ LRT_ITER_ALL_LINES_BEGIN
+ {
+ if (!(e->flags & (LRT_EDGE_FLAG_CONTOUR | LRT_EDGE_FLAG_LOOSE))) {
+ continue;
+ }
+ LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) {
+ DISCARD_NONSENSE_SEGMENTS
+
+ double next_at = es->next ? ((LineartEdgeSegment *)es->next)->ratio : 1.0f;
+ /* Get correct XYZ and W coordinates. */
+ interp_v3_v3v3_db(sedge[i].fbc1, e->v1->fbcoord, e->v2->fbcoord, es->ratio);
+ interp_v3_v3v3_db(sedge[i].fbc2, e->v1->fbcoord, e->v2->fbcoord, next_at);
+
+ /* Global coord for light-shadow separation line (occlusion-corrected light contour). */
+ double ga1 = e->v1->fbcoord[3] * es->ratio /
+ (es->ratio * e->v1->fbcoord[3] + (1 - es->ratio) * e->v2->fbcoord[3]);
+ double ga2 = e->v1->fbcoord[3] * next_at /
+ (next_at * e->v1->fbcoord[3] + (1 - next_at) * e->v2->fbcoord[3]);
+ interp_v3_v3v3_db(sedge[i].g1, e->v1->gloc, e->v2->gloc, ga1);
+ interp_v3_v3v3_db(sedge[i].g2, e->v1->gloc, e->v2->gloc, ga2);
+
+ /* Assign an absurdly big W for initial distance so when triangles show up to catch the
+ * shadow, their w must certainly be smaller than this value so the shadow catches
+ * successfully. */
+ sedge[i].fbc1[3] = 1e30;
+ sedge[i].fbc2[3] = 1e30;
+ sedge[i].fbc1[2] = 1e30;
+ sedge[i].fbc2[2] = 1e30;
+
+ /* Assign to the first segment's right and the last segment's left position */
+ copy_v4_v4_db(sseg[i * 2].fbc2, sedge[i].fbc1);
+ copy_v4_v4_db(sseg[i * 2 + 1].fbc1, sedge[i].fbc2);
+ sseg[i * 2].ratio = 0.0f;
+ sseg[i * 2 + 1].ratio = 1.0f;
+ BLI_addtail(&sedge[i].shadow_segments, &sseg[i * 2]);
+ BLI_addtail(&sedge[i].shadow_segments, &sseg[i * 2 + 1]);
+
+ if (e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) {
+ sedge[i].e_ref = (LineartEdge *)e->t1;
+ sedge[i].e_ref_light_contour = e;
+ /* Restore original edge flag for edges "who is both view and light contour" so we still
+ * have correct edge flags. */
+ e->flags &= (~LRT_EDGE_FLAG_CONTOUR);
+ }
+ else {
+ sedge[i].e_ref = e;
+ }
+
+ sedge[i].es_ref = es;
+
+ i++;
+ }
+ }
+ LRT_ITER_ALL_LINES_END
+
+ /* Transform the cutting position to global space for regular feature lines. This is for
+ * convenience of reusing the shadow cast function for both shadow line generation and silhouette
+ * registration, which the latter one needs view-space coordinates, while cast shadow needs
+ * global-space coordinates. */
+ if (transform_edge_cuts) {
+ LRT_ITER_ALL_LINES_BEGIN
+ {
+ LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) {
+ es->ratio = e->v1->fbcoord[3] * es->ratio /
+ (es->ratio * e->v1->fbcoord[3] + (1 - es->ratio) * e->v2->fbcoord[3]);
+ }
+ }
+ LRT_ITER_ALL_LINES_END
+ }
+
+ if (G.debug_value == 4000) {
+ printf("Shadow: Added %d raw shadow_edges\n", segment_count);
+ }
+}
+
+/* This function does the actual cutting on a given "shadow edge".
+ * #start / #end determines the view(from light camera) space cutting ratio.
+ * #start/end_gloc/fbc are the respective start/end coordinates.
+ * #facing_light is set from the caller which determines if this edge landed on a triangle's light
+ * facing side or not.
+ *
+ * Visually this function does this: (Top is the far side of the camera)
+ * _-end
+ * _-`
+ * l[-------------_-`--------------]r [e] 1) Calls for cut on top of #e.
+ * _-`
+ * _-`
+ * start-`
+ *
+ * _-end
+ * _-`
+ * l[-----][------_-`----][--------]r [e] 2) Add cutting points on #e at #start/#end.
+ * _-`
+ * _-`
+ * start-`
+ *
+ * _-end
+ * _-`
+ * [------_-`----] 3) Call lineart_shadow_segment_slice_get() to
+ * _-` get coordinates of a visually aligned segment on
+ * _-` #e with the incoming segment.
+ * start-`
+ *
+ * _c-----] 4) Call lineart_do_closest_segment() to find out the
+ * _-` actual geometry after cut, add a new cut if needed.
+ * _-`
+ * [`
+ *
+ * l[-----] _][----][--------]r [e] 5) Write coordinates on cuts.
+ * _-`
+ * _-`
+ * [`
+ *
+ * This process is repeated on each existing segments of the shadow edge (#e), which ensures they
+ * all have been tested for closest segments after cutting. And in the diagram it's clear that the
+ * left/right side of cuts are likely to be discontinuous, each cut's left side designates the
+ * right side of the last segment, and vice-versa. */
+static void lineart_shadow_edge_cut(LineartData *ld,
+ LineartShadowEdge *e,
+ double start,
+ double end,
+ double *start_gloc,
+ double *end_gloc,
+ double *start_fb_co,
+ double *end_fb_co,
+ bool facing_light,
+ uint32_t target_reference)
+{
+ LineartShadowSegment *seg, *i_seg;
+ LineartShadowSegment *cut_start_after = e->shadow_segments.first,
+ *cut_end_before = e->shadow_segments.last;
+ LineartShadowSegment *new_seg_1 = NULL, *new_seg_2 = NULL, *seg_1 = NULL, *seg_2 = NULL;
+ int untouched = 0;
+
+ /* If for some reason the occlusion function may give a result that has zero length, or
+ * reversed in direction, or NAN, we take care of them here. */
+ if (LRT_DOUBLE_CLOSE_ENOUGH(start, end)) {
+ return;
+ }
+ if (LRT_DOUBLE_CLOSE_ENOUGH(start, 1) || LRT_DOUBLE_CLOSE_ENOUGH(end, 0)) {
+ return;
+ }
+ if (UNLIKELY(start != start)) {
+ start = 0;
+ }
+ if (UNLIKELY(end != end)) {
+ end = 0;
+ }
+
+ if (start > end) {
+ double t = start;
+ start = end;
+ end = t;
+ }
+
+ /* Begin looking for starting position of the segment. */
+ /* Not using a list iteration macro because of it more clear when using for loops to iterate
+ * through the segments. */
+ for (seg = e->shadow_segments.first; seg; seg = seg->next) {
+ if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, start)) {
+ cut_start_after = seg;
+ new_seg_1 = cut_start_after;
+ break;
+ }
+ if (seg->next == NULL) {
+ break;
+ }
+ i_seg = seg->next;
+ if (i_seg->ratio > start + 1e-09 && start > seg->ratio) {
+ cut_start_after = seg;
+ new_seg_1 = lineart_give_shadow_segment(ld);
+ break;
+ }
+ }
+ if (!cut_start_after && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ untouched = 1;
+ }
+ for (seg = cut_start_after->next; seg; seg = seg->next) {
+ /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle
+ * strip). */
+ if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, end)) {
+ cut_end_before = seg;
+ new_seg_2 = cut_end_before;
+ break;
+ }
+ /* This check is to prevent `es->ratio == 1.0` (where we don't need to cut because we are ratio
+ * the end point). */
+ if (!seg->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ cut_end_before = seg;
+ new_seg_2 = cut_end_before;
+ untouched = 1;
+ break;
+ }
+ /* When an actual cut is needed in the line. */
+ if (seg->ratio > end) {
+ cut_end_before = seg;
+ new_seg_2 = lineart_give_shadow_segment(ld);
+ break;
+ }
+ }
+
+ /* When we still can't find any existing cut in the line, we allocate new ones. */
+ if (new_seg_1 == NULL) {
+ new_seg_1 = lineart_give_shadow_segment(ld);
+ }
+ if (new_seg_2 == NULL) {
+ if (untouched) {
+ new_seg_2 = new_seg_1;
+ cut_end_before = new_seg_2;
+ }
+ else {
+ new_seg_2 = lineart_give_shadow_segment(ld);
+ }
+ }
+
+ /* If we touched the cut list, we assign the new cut position based on new cut position,
+ * this way we accommodate precision lost due to multiple cut inserts. */
+ new_seg_1->ratio = start;
+ if (!untouched) {
+ new_seg_2->ratio = end;
+ }
+
+ double r_fb_co_1[4], r_fb_co_2[4], r_gloc_1[3], r_gloc_2[3];
+ double r_new_in_the_middle[4], r_new_in_the_middle_global[3], r_new_at;
+ double *s1_fb_co_1, *s1_fb_co_2, *s1_gloc_1, *s1_gloc_2;
+
+ /* Temporary coordinate records and "middle" records. */
+ double t_g1[3], t_g2[3], t_fbc1[4], t_fbc2[4], m_g1[3], m_fbc1[4], m_g2[3], m_fbc2[4];
+ bool is_side_2r, has_middle = false, use_new_ref;
+ copy_v4_v4_db(t_fbc1, start_fb_co);
+ copy_v3_v3_db(t_g1, start_gloc);
+
+ /* Do max stuff before insert. */
+ LineartShadowSegment *nes;
+ for (seg = cut_start_after; seg != cut_end_before; seg = nes) {
+ nes = seg->next;
+
+ s1_fb_co_1 = seg->fbc2, s1_fb_co_2 = nes->fbc1;
+ s1_gloc_1 = seg->g2, s1_gloc_2 = nes->g1;
+ seg_1 = seg, seg_2 = nes;
+ if (seg == cut_start_after) {
+ lineart_shadow_segment_slice_get(seg->fbc2,
+ nes->fbc1,
+ seg->g2,
+ nes->g1,
+ new_seg_1->ratio,
+ seg->ratio,
+ nes->ratio,
+ m_fbc1,
+ m_g1);
+ s1_fb_co_1 = m_fbc1, s1_gloc_1 = m_g1;
+ seg_1 = new_seg_1;
+ if (cut_start_after != new_seg_1) {
+ BLI_insertlinkafter(&e->shadow_segments, cut_start_after, new_seg_1);
+ copy_v4_v4_db(new_seg_1->fbc1, m_fbc1);
+ copy_v3_v3_db(new_seg_1->g1, m_g1);
+ }
+ }
+ if (nes == cut_end_before) {
+ lineart_shadow_segment_slice_get(seg->fbc2,
+ nes->fbc1,
+ seg->g2,
+ nes->g1,
+ new_seg_2->ratio,
+ seg->ratio,
+ nes->ratio,
+ m_fbc2,
+ m_g2);
+ s1_fb_co_2 = m_fbc2, s1_gloc_2 = m_g2;
+ seg_2 = new_seg_2;
+ if (cut_end_before != new_seg_2) {
+ BLI_insertlinkbefore(&e->shadow_segments, cut_end_before, new_seg_2);
+ copy_v4_v4_db(new_seg_2->fbc2, m_fbc2);
+ copy_v3_v3_db(new_seg_2->g2, m_g2);
+ /* Need to restore the flag for next segment's reference. */
+ seg_2->flag = seg->flag;
+ seg_2->target_reference = seg->target_reference;
+ }
+ }
+
+ lineart_shadow_segment_slice_get(
+ start_fb_co, end_fb_co, start_gloc, end_gloc, seg_2->ratio, start, end, t_fbc2, t_g2);
+
+ if ((has_middle = lineart_do_closest_segment(ld->conf.cam_is_persp,
+ s1_fb_co_1,
+ s1_fb_co_2,
+ t_fbc1,
+ t_fbc2,
+ s1_gloc_1,
+ s1_gloc_2,
+ t_g1,
+ t_g2,
+ r_fb_co_1,
+ r_fb_co_2,
+ r_gloc_1,
+ r_gloc_2,
+ r_new_in_the_middle,
+ r_new_in_the_middle_global,
+ &r_new_at,
+ &is_side_2r,
+ &use_new_ref))) {
+ LineartShadowSegment *ss_middle = lineart_give_shadow_segment(ld);
+ ss_middle->ratio = interpf(seg_2->ratio, seg_1->ratio, r_new_at);
+ ss_middle->flag = LRT_SHADOW_CASTED |
+ (use_new_ref ? (facing_light ? LRT_SHADOW_FACING_LIGHT : 0) : seg_1->flag);
+ ss_middle->target_reference = (use_new_ref ? (target_reference) : seg_1->target_reference);
+ copy_v3_v3_db(ss_middle->g1, r_new_in_the_middle_global);
+ copy_v3_v3_db(ss_middle->g2, r_new_in_the_middle_global);
+ copy_v4_v4_db(ss_middle->fbc1, r_new_in_the_middle);
+ copy_v4_v4_db(ss_middle->fbc2, r_new_in_the_middle);
+ BLI_insertlinkafter(&e->shadow_segments, seg_1, ss_middle);
+ }
+ /* Always assign the "closest" value to the segment. */
+ copy_v4_v4_db(seg_1->fbc2, r_fb_co_1);
+ copy_v3_v3_db(seg_1->g2, r_gloc_1);
+ copy_v4_v4_db(seg_2->fbc1, r_fb_co_2);
+ copy_v3_v3_db(seg_2->g1, r_gloc_2);
+
+ if (has_middle) {
+ seg_1->flag = LRT_SHADOW_CASTED |
+ (is_side_2r ? seg->flag : (facing_light ? LRT_SHADOW_FACING_LIGHT : 0));
+ seg_1->target_reference = is_side_2r ? seg->target_reference : target_reference;
+ }
+ else {
+ seg_1->flag = LRT_SHADOW_CASTED |
+ (use_new_ref ? (facing_light ? LRT_SHADOW_FACING_LIGHT : 0) : seg->flag);
+ seg_1->target_reference = use_new_ref ? target_reference : seg->target_reference;
+ }
+
+ copy_v4_v4_db(t_fbc1, t_fbc2);
+ copy_v3_v3_db(t_g1, t_g2);
+ }
+}
+
+static bool lineart_shadow_cast_onto_triangle(LineartData *ld,
+ LineartTriangle *tri,
+ LineartShadowEdge *sedge,
+ double *r_at_1,
+ double *r_at_2,
+ double *r_fb_co_1,
+ double *r_fb_co_2,
+ double *r_gloc_1,
+ double *r_gloc_2,
+ bool *r_facing_light)
+{
+
+ double *LFBC = sedge->fbc1, *RFBC = sedge->fbc2, *FBC0 = tri->v[0]->fbcoord,
+ *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord;
+
+ /* Bound box check. Because we have already done occlusion in the shadow camera, so any visual
+ * intersection found in this function must mean that the triangle is behind the given line so it
+ * will always project a shadow, hence no need to do depth bound-box check. */
+ if ((MAX3(FBC0[0], FBC1[0], FBC2[0]) < MIN2(LFBC[0], RFBC[0])) ||
+ (MIN3(FBC0[0], FBC1[0], FBC2[0]) > MAX2(LFBC[0], RFBC[0])) ||
+ (MAX3(FBC0[1], FBC1[1], FBC2[1]) < MIN2(LFBC[1], RFBC[1])) ||
+ (MIN3(FBC0[1], FBC1[1], FBC2[1]) > MAX2(LFBC[1], RFBC[1]))) {
+ return false;
+ }
+
+ bool is_persp = ld->conf.cam_is_persp;
+ double ratio[2];
+ int trie[2];
+ int pi = 0;
+ if (lineart_line_isec_2d_ignore_line2pos(FBC0, FBC1, LFBC, RFBC, &ratio[pi])) {
+ trie[pi] = 0;
+ pi++;
+ }
+ if (lineart_line_isec_2d_ignore_line2pos(FBC1, FBC2, LFBC, RFBC, &ratio[pi])) {
+ /* ratio[0] == 1 && ratio[1] == 0 means we found a intersection at the same point of the
+ * edge (FBC1), ignore this one and try get the intersection point from the other side of
+ * the edge
+ */
+ if (!(pi && LRT_DOUBLE_CLOSE_ENOUGH(ratio[0], 1.0f) &&
+ LRT_DOUBLE_CLOSE_ENOUGH(ratio[1], 0.0f))) {
+ trie[pi] = 1;
+ pi++;
+ }
+ }
+ if (!pi) {
+ return false;
+ }
+ if (pi == 1 && lineart_line_isec_2d_ignore_line2pos(FBC2, FBC0, LFBC, RFBC, &ratio[pi])) {
+
+ if ((trie[0] == 0 && LRT_DOUBLE_CLOSE_ENOUGH(ratio[0], 0.0f) &&
+ LRT_DOUBLE_CLOSE_ENOUGH(ratio[1], 1.0f)) ||
+ (trie[0] == 1 && LRT_DOUBLE_CLOSE_ENOUGH(ratio[0], 1.0f) &&
+ LRT_DOUBLE_CLOSE_ENOUGH(ratio[1], 0.0f))) {
+ return false;
+ }
+ trie[pi] = 2;
+ pi++;
+ }
+
+ if (pi != 2) {
+ return false;
+ }
+
+ /* Get projected global position. */
+
+ double gpos1[3], gpos2[3];
+ double *v1 = (trie[0] == 0 ? FBC0 : (trie[0] == 1 ? FBC1 : FBC2));
+ double *v2 = (trie[0] == 0 ? FBC1 : (trie[0] == 1 ? FBC2 : FBC0));
+ double *v3 = (trie[1] == 0 ? FBC0 : (trie[1] == 1 ? FBC1 : FBC2));
+ double *v4 = (trie[1] == 0 ? FBC1 : (trie[1] == 1 ? FBC2 : FBC0));
+ double *gv1 = (trie[0] == 0 ? tri->v[0]->gloc :
+ (trie[0] == 1 ? tri->v[1]->gloc : tri->v[2]->gloc));
+ double *gv2 = (trie[0] == 0 ? tri->v[1]->gloc :
+ (trie[0] == 1 ? tri->v[2]->gloc : tri->v[0]->gloc));
+ double *gv3 = (trie[1] == 0 ? tri->v[0]->gloc :
+ (trie[1] == 1 ? tri->v[1]->gloc : tri->v[2]->gloc));
+ double *gv4 = (trie[1] == 0 ? tri->v[1]->gloc :
+ (trie[1] == 1 ? tri->v[2]->gloc : tri->v[0]->gloc));
+ double gr1 = is_persp ? v1[3] * ratio[0] / (ratio[0] * v1[3] + (1 - ratio[0]) * v2[3]) :
+ ratio[0];
+ double gr2 = is_persp ? v3[3] * ratio[1] / (ratio[1] * v3[3] + (1 - ratio[1]) * v4[3]) :
+ ratio[1];
+ interp_v3_v3v3_db(gpos1, gv1, gv2, gr1);
+ interp_v3_v3v3_db(gpos2, gv3, gv4, gr2);
+
+ double fbc1[4], fbc2[4];
+
+ mul_v4_m4v3_db(fbc1, ld->conf.view_projection, gpos1);
+ mul_v4_m4v3_db(fbc2, ld->conf.view_projection, gpos2);
+ if (is_persp) {
+ mul_v3db_db(fbc1, 1.0f / fbc1[3]);
+ mul_v3db_db(fbc2, 1.0f / fbc2[3]);
+ }
+
+ int use = (fabs(LFBC[0] - RFBC[0]) > fabs(LFBC[1] - RFBC[1])) ? 0 : 1;
+ double at1 = ratiod(LFBC[use], RFBC[use], fbc1[use]);
+ double at2 = ratiod(LFBC[use], RFBC[use], fbc2[use]);
+ if (at1 > at2) {
+ swap_v3_v3_db(gpos1, gpos2);
+ swap_v4_v4_db(fbc1, fbc2);
+ SWAP(double, at1, at2);
+ }
+
+ /* If not effectively projecting anything. */
+ if (at1 > (1.0f - FLT_EPSILON) || at2 < FLT_EPSILON) {
+ return false;
+ }
+
+ /* Trim to edge's end points. */
+
+ double t_fbc1[4], t_fbc2[4], t_gpos1[3], t_gpos2[3];
+ bool trimmed1 = false, trimmed2 = false;
+ if (at1 < 0 || at2 > 1) {
+ double rat1 = (-at1) / (at2 - at1);
+ double rat2 = (1.0f - at1) / (at2 - at1);
+ double gat1 = is_persp ? fbc1[3] * rat1 / (rat1 * fbc1[3] + (1 - rat1) * fbc2[3]) : rat1;
+ double gat2 = is_persp ? fbc1[3] * rat2 / (rat2 * fbc1[3] + (1 - rat2) * fbc2[3]) : rat2;
+ if (at1 < 0) {
+ interp_v3_v3v3_db(t_gpos1, gpos1, gpos2, gat1);
+ interp_v3_v3v3_db(t_fbc1, fbc1, fbc2, rat1);
+ t_fbc1[3] = interpd(fbc2[3], fbc1[3], gat1);
+ at1 = 0, trimmed1 = true;
+ }
+ if (at2 > 1) {
+ interp_v3_v3v3_db(t_gpos2, gpos1, gpos2, gat2);
+ interp_v3_v3v3_db(t_fbc2, fbc1, fbc2, rat2);
+ t_fbc2[3] = interpd(fbc2[3], fbc1[3], gat2);
+ at2 = 1, trimmed2 = true;
+ }
+ }
+ if (trimmed1) {
+ copy_v4_v4_db(fbc1, t_fbc1);
+ copy_v3_v3_db(gpos1, t_gpos1);
+ }
+ if (trimmed2) {
+ copy_v4_v4_db(fbc2, t_fbc2);
+ copy_v3_v3_db(gpos2, t_gpos2);
+ }
+
+ *r_at_1 = at1;
+ *r_at_2 = at2;
+ copy_v4_v4_db(r_fb_co_1, fbc1);
+ copy_v4_v4_db(r_fb_co_2, fbc2);
+ copy_v3_v3_db(r_gloc_1, gpos1);
+ copy_v3_v3_db(r_gloc_2, gpos2);
+
+ double camera_vector[3];
+
+ if (is_persp) {
+ sub_v3_v3v3_db(camera_vector, ld->conf.camera_pos, tri->v[0]->gloc);
+ }
+ else {
+ copy_v3_v3_db(camera_vector, ld->conf.view_vector);
+ }
+
+ double dot_f = dot_v3v3_db(camera_vector, tri->gn);
+ *r_facing_light = (dot_f < 0);
+
+ return true;
+}
+
+/* The one step all to cast all visible edges in light camera back to other geometries behind them,
+ * the result of this step can then be generated as actual LineartEdge's for occlusion test in view
+ * camera. */
+static void lineart_shadow_cast(LineartData *ld, bool transform_edge_cuts, bool do_light_contour)
+{
+
+ lineart_shadow_create_shadow_edge_array(ld, transform_edge_cuts, do_light_contour);
+
+ /* Keep it single threaded for now because the loop will write "done" pointers to triangles. */
+ for (int edge_i = 0; edge_i < ld->shadow_edges_count; edge_i++) {
+ LineartShadowEdge *sedge = &ld->shadow_edges[edge_i];
+
+ LineartTriangleThread *tri;
+ double at_1, at_2;
+ double fb_co_1[4], fb_co_2[4];
+ double global_1[3], global_2[3];
+ bool facing_light;
+
+ LRT_EDGE_BA_MARCHING_BEGIN(sedge->fbc1, sedge->fbc2)
+ {
+ for (int i = 0; i < nba->triangle_count; i++) {
+ tri = (LineartTriangleThread *)nba->linked_triangles[i];
+ if (tri->testing_e[0] == (LineartEdge *)sedge || tri->base.mat_occlusion == 0 ||
+ lineart_edge_from_triangle(
+ (LineartTriangle *)tri, sedge->e_ref, ld->conf.allow_overlapping_edges)) {
+ continue;
+ }
+ tri->testing_e[0] = (LineartEdge *)sedge;
+
+ if (lineart_shadow_cast_onto_triangle(ld,
+ (LineartTriangle *)tri,
+ sedge,
+ &at_1,
+ &at_2,
+ fb_co_1,
+ fb_co_2,
+ global_1,
+ global_2,
+ &facing_light)) {
+ lineart_shadow_edge_cut(ld,
+ sedge,
+ at_1,
+ at_2,
+ global_1,
+ global_2,
+ fb_co_1,
+ fb_co_2,
+ facing_light,
+ tri->base.target_reference);
+ }
+ }
+ LRT_EDGE_BA_MARCHING_NEXT(sedge->fbc1, sedge->fbc2);
+ }
+ LRT_EDGE_BA_MARCHING_END;
+ }
+}
+
+/* For each [segment] on a shadow shadow_edge, 1 LineartEdge will be generated with a cast shadow
+ * edge flag (if that segment failed to cast onto anything then it's not generated). The original
+ * shadow shadow_edge is optionally generated as a light contour. */
+static bool lineart_shadow_cast_generate_edges(LineartData *ld,
+ bool do_original_edges,
+ LineartElementLinkNode **r_veln,
+ LineartElementLinkNode **r_eeln)
+{
+ int tot_edges = 0;
+ int tot_orig_edges = 0;
+ for (int i = 0; i < ld->shadow_edges_count; i++) {
+ LineartShadowEdge *sedge = &ld->shadow_edges[i];
+ LISTBASE_FOREACH (LineartShadowSegment *, sseg, &sedge->shadow_segments) {
+ if (!(sseg->flag & LRT_SHADOW_CASTED)) {
+ continue;
+ }
+ if (!sseg->next) {
+ break;
+ }
+ tot_edges++;
+ }
+ tot_orig_edges++;
+ }
+
+ int edge_alloc = tot_edges + (do_original_edges ? tot_orig_edges : 0);
+
+ if (G.debug_value == 4000) {
+ printf("Line art shadow segments total: %d\n", tot_edges);
+ }
+
+ if (!edge_alloc) {
+ return false;
+ }
+ LineartElementLinkNode *veln = lineart_mem_acquire(ld->shadow_data_pool,
+ sizeof(LineartElementLinkNode));
+ LineartElementLinkNode *eeln = lineart_mem_acquire(ld->shadow_data_pool,
+ sizeof(LineartElementLinkNode));
+ veln->pointer = lineart_mem_acquire(ld->shadow_data_pool, sizeof(LineartVert) * edge_alloc * 2);
+ eeln->pointer = lineart_mem_acquire(ld->shadow_data_pool, sizeof(LineartEdge) * edge_alloc);
+ LineartEdgeSegment *es = lineart_mem_acquire(ld->shadow_data_pool,
+ sizeof(LineartEdgeSegment) * edge_alloc);
+ *r_veln = veln;
+ *r_eeln = eeln;
+
+ veln->element_count = edge_alloc * 2;
+ eeln->element_count = edge_alloc;
+
+ LineartVert *vlist = veln->pointer;
+ LineartEdge *elist = eeln->pointer;
+
+ int ei = 0;
+ for (int i = 0; i < ld->shadow_edges_count; i++) {
+ LineartShadowEdge *sedge = &ld->shadow_edges[i];
+ LISTBASE_FOREACH (LineartShadowSegment *, sseg, &sedge->shadow_segments) {
+ if (!(sseg->flag & LRT_SHADOW_CASTED)) {
+ continue;
+ }
+ if (!sseg->next) {
+ break;
+ }
+ LineartEdge *e = &elist[ei];
+ BLI_addtail(&e->segments, &es[ei]);
+ LineartVert *v1 = &vlist[ei * 2], *v2 = &vlist[ei * 2 + 1];
+ copy_v3_v3_db(v1->gloc, sseg->g2);
+ copy_v3_v3_db(v2->gloc, ((LineartShadowSegment *)sseg->next)->g1);
+ e->v1 = v1;
+ e->v2 = v2;
+ e->t1 = (LineartTriangle *)sedge->e_ref; /* See LineartEdge::t1 for usage. */
+ e->t2 = (LineartTriangle *)(sedge->e_ref_light_contour ? sedge->e_ref_light_contour :
+ sedge->e_ref);
+ e->target_reference = sseg->target_reference;
+ e->edge_identifier = sedge->e_ref->edge_identifier;
+ e->flags = (LRT_EDGE_FLAG_PROJECTED_SHADOW |
+ ((sseg->flag & LRT_SHADOW_FACING_LIGHT) ? LRT_EDGE_FLAG_SHADOW_FACING_LIGHT :
+ 0));
+ ei++;
+ }
+ if (do_original_edges) {
+ /* Occlusion-corrected light contour. */
+ LineartEdge *e = &elist[ei];
+ BLI_addtail(&e->segments, &es[ei]);
+ LineartVert *v1 = &vlist[ei * 2], *v2 = &vlist[ei * 2 + 1];
+ v1->index = sedge->e_ref->v1->index;
+ v2->index = sedge->e_ref->v2->index;
+ copy_v3_v3_db(v1->gloc, sedge->g1);
+ copy_v3_v3_db(v2->gloc, sedge->g2);
+ uint64_t ref_1 = sedge->e_ref->t1 ? sedge->e_ref->t1->target_reference : 0;
+ uint64_t ref_2 = sedge->e_ref->t2 ? sedge->e_ref->t2->target_reference : 0;
+ e->edge_identifier = sedge->e_ref->edge_identifier;
+ e->target_reference = ((ref_1 << 32) | ref_2);
+ e->v1 = v1;
+ e->v2 = v2;
+ e->t1 = e->t2 = (LineartTriangle *)sedge->e_ref;
+ e->flags = LRT_EDGE_FLAG_LIGHT_CONTOUR;
+ if (lineart_contour_viewed_from_dark_side(ld, sedge->e_ref)) {
+ lineart_edge_cut(ld, e, 0.0f, 1.0f, 0, 0, LRT_SHADOW_MASK_SHADED);
+ }
+ ei++;
+ }
+ }
+ return true;
+}
+
+static void lineart_shadow_register_silhouette(LineartData *ld)
+{
+ /* Keeping it single threaded for now because a simple parallel_for could end up getting the same
+ * #sedge->e_ref in different threads. */
+ for (int i = 0; i < ld->shadow_edges_count; i++) {
+ LineartShadowEdge *sedge = &ld->shadow_edges[i];
+
+ LineartEdge *e = sedge->e_ref;
+ LineartEdgeSegment *es = sedge->es_ref;
+ double es_start = es->ratio, es_end = es->next ? es->next->ratio : 1.0f;
+ LISTBASE_FOREACH (LineartShadowSegment *, sseg, &sedge->shadow_segments) {
+ if (!(sseg->flag & LRT_SHADOW_CASTED)) {
+ continue;
+ }
+ if (!sseg->next) {
+ break;
+ }
+
+ uint32_t silhouette_flags = (sseg->target_reference & LRT_OBINDEX_HIGHER) |
+ LRT_SHADOW_SILHOUETTE_ERASED_GROUP;
+
+ double at_start = interpd(es_end, es_start, sseg->ratio);
+ double at_end = interpd(es_end, es_start, sseg->next->ratio);
+ lineart_edge_cut(ld, e, at_start, at_end, 0, 0, silhouette_flags);
+ }
+ }
+}
+
+/* To achieve enclosed shape effect, we need to:
+ * 1) Show shaded segments against lit background.
+ * 2) Erase lit segments against lit background. */
+static void lineart_shadow_register_enclosed_shapes(LineartData *ld, LineartData *shadow_ld)
+{
+ LineartEdge *e;
+ LineartEdgeSegment *es;
+ for (int i = 0; i < shadow_ld->pending_edges.next; i++) {
+ e = shadow_ld->pending_edges.array[i];
+
+ /* Only care about shade-on-light and light-on-light situations, hence we only need
+ * non-occluded segments in shadow buffer. */
+ if (e->min_occ > 0) {
+ continue;
+ }
+ for (es = e->segments.first; es; es = es->next) {
+ if (es->occlusion > 0) {
+ continue;
+ }
+ double next_at = es->next ? ((LineartEdgeSegment *)es->next)->ratio : 1.0f;
+ LineartEdge *orig_e = (LineartEdge *)e->t2;
+
+ /* Shadow view space to global. */
+ double ga1 = e->v1->fbcoord[3] * es->ratio /
+ (es->ratio * e->v1->fbcoord[3] + (1 - es->ratio) * e->v2->fbcoord[3]);
+ double ga2 = e->v1->fbcoord[3] * next_at /
+ (next_at * e->v1->fbcoord[3] + (1 - next_at) * e->v2->fbcoord[3]);
+ double g1[3], g2[3], g1v[4], g2v[4];
+ interp_v3_v3v3_db(g1, e->v1->gloc, e->v2->gloc, ga1);
+ interp_v3_v3v3_db(g2, e->v1->gloc, e->v2->gloc, ga2);
+ mul_v4_m4v3_db(g1v, ld->conf.view_projection, g1);
+ mul_v4_m4v3_db(g2v, ld->conf.view_projection, g2);
+
+ if (ld->conf.cam_is_persp) {
+ mul_v3db_db(g1v, (1 / g1v[3]));
+ mul_v3db_db(g2v, (1 / g2v[3]));
+ }
+
+ g1v[0] -= ld->conf.shift_x * 2;
+ g1v[1] -= ld->conf.shift_y * 2;
+ g2v[0] -= ld->conf.shift_x * 2;
+ g2v[1] -= ld->conf.shift_y * 2;
+
+#define GET_RATIO(n) \
+ (fabs(orig_e->v2->fbcoord[0] - orig_e->v1->fbcoord[0]) > \
+ fabs(orig_e->v2->fbcoord[1] - orig_e->v1->fbcoord[1])) ? \
+ ((g##n##v[0] - orig_e->v1->fbcoord[0]) / \
+ (orig_e->v2->fbcoord[0] - orig_e->v1->fbcoord[0])) : \
+ ((g##n##v[1] - orig_e->v1->fbcoord[1]) / (orig_e->v2->fbcoord[1] - orig_e->v1->fbcoord[1]))
+ double la1, la2;
+ la1 = GET_RATIO(1);
+ la2 = GET_RATIO(2);
+#undef GET_RATIO
+
+ lineart_edge_cut(ld, orig_e, la1, la2, 0, 0, LRT_SHADOW_MASK_ENCLOSED_SHAPE);
+ }
+ }
+}
+
+/* This call would internally duplicate #original_ld, override necessary configurations for shadow
+ * computations. It will return:
+ *
+ * 1) Generated shadow edges in format of `LineartElementLinkNode` which can be directly loaded
+ * into later main view camera occlusion stage.
+ * 2) Shadow render buffer if 3rd stage reprojection is need for silhouette/lit/shaded region
+ * selection. Otherwise the shadow render buffer is deleted before this function returns.
+ */
+bool lineart_main_try_generate_shadow(Depsgraph *depsgraph,
+ Scene *scene,
+ LineartData *original_ld,
+ LineartGpencilModifierData *lmd,
+ LineartStaticMemPool *shadow_data_pool,
+ LineartElementLinkNode **r_veln,
+ LineartElementLinkNode **r_eeln,
+ ListBase *r_calculated_edges_eln_list,
+ LineartData **r_shadow_ld_if_reproject)
+{
+ if ((!original_ld->conf.use_shadow && !original_ld->conf.use_light_contour &&
+ !original_ld->conf.shadow_selection) ||
+ (!lmd->light_contour_object)) {
+ return false;
+ }
+
+ double t_start;
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
+ bool is_persp = true;
+
+ if (lmd->light_contour_object->type == OB_LAMP) {
+ Light *la = (Light *)lmd->light_contour_object->data;
+ if (la->type == LA_SUN) {
+ is_persp = false;
+ }
+ }
+
+ LineartData *ld = MEM_callocN(sizeof(LineartData), "LineArt render buffer copied");
+ memcpy(ld, original_ld, sizeof(LineartData));
+
+ BLI_spin_init(&ld->lock_task);
+ BLI_spin_init(&ld->lock_cuts);
+ BLI_spin_init(&ld->render_data_pool.lock_mem);
+
+ ld->conf.do_shadow_cast = true;
+ ld->shadow_data_pool = shadow_data_pool;
+
+ /* See LineartData::edge_data_pool for explanation. */
+ if (ld->conf.shadow_selection) {
+ ld->edge_data_pool = shadow_data_pool;
+ }
+ else {
+ ld->edge_data_pool = &ld->render_data_pool;
+ }
+
+ copy_v3_v3_db(ld->conf.camera_pos_secondary, ld->conf.camera_pos);
+ copy_m4_m4(ld->conf.cam_obmat_secondary, ld->conf.cam_obmat);
+
+ copy_m4_m4(ld->conf.cam_obmat, lmd->light_contour_object->obmat);
+ copy_v3db_v3fl(ld->conf.camera_pos, ld->conf.cam_obmat[3]);
+ ld->conf.cam_is_persp_secondary = ld->conf.cam_is_persp;
+ ld->conf.cam_is_persp = is_persp;
+ ld->conf.near_clip = is_persp ? lmd->shadow_camera_near : -lmd->shadow_camera_far;
+ ld->conf.far_clip = lmd->shadow_camera_far;
+ ld->w = lmd->shadow_camera_size;
+ ld->h = lmd->shadow_camera_size;
+ /* Need to prevent wrong camera configuration so that shadow computation won't stall. */
+ if (!ld->w || !ld->h) {
+ ld->w = ld->h = 200;
+ }
+ if (!ld->conf.near_clip || !ld->conf.far_clip) {
+ ld->conf.near_clip = 0.1f;
+ ld->conf.far_clip = 200.0f;
+ }
+ ld->qtree.recursive_level = is_persp ? LRT_TILE_RECURSIVE_PERSPECTIVE : LRT_TILE_RECURSIVE_ORTHO;
+
+ /* Contour and loose edge from light viewing direction will be cast as shadow, so only
+ * force them on. If we need lit/shaded information for other line types, they are then
+ * enabled as-is so that cutting positions can also be calculated through shadow projection.
+ */
+ if (!ld->conf.shadow_selection) {
+ ld->conf.use_crease = ld->conf.use_material = ld->conf.use_edge_marks =
+ ld->conf.use_intersections = ld->conf.use_light_contour = false;
+ }
+ else {
+ ld->conf.use_contour_secondary = true;
+ ld->conf.allow_duplicated_types = true;
+ }
+ ld->conf.use_loose = true;
+ ld->conf.use_contour = true;
+
+ ld->conf.max_occlusion_level = 0; /* No point getting see-through projections there. */
+ ld->conf.use_back_face_culling = false;
+
+ /* Override matrices to light "camera". */
+ double proj[4][4], view[4][4], result[4][4];
+ float inv[4][4];
+ if (is_persp) {
+ lineart_matrix_perspective_44d(proj, DEG2RAD(160), 1, ld->conf.near_clip, ld->conf.far_clip);
+ }
+ else {
+ lineart_matrix_ortho_44d(
+ proj, -ld->w, ld->w, -ld->h, ld->h, ld->conf.near_clip, ld->conf.far_clip);
+ }
+ invert_m4_m4(inv, ld->conf.cam_obmat);
+ mul_m4db_m4db_m4fl_uniq(result, proj, inv);
+ copy_m4_m4_db(proj, result);
+ copy_m4_m4_db(ld->conf.view_projection, proj);
+ unit_m4_db(view);
+ copy_m4_m4_db(ld->conf.view, view);
+
+ lineart_main_get_view_vector(ld);
+
+ lineart_main_load_geometries(
+ depsgraph, scene, NULL, ld, lmd->flags & LRT_ALLOW_DUPLI_OBJECTS, true, NULL);
+
+ if (!ld->geom.vertex_buffer_pointers.first) {
+ /* No geometry loaded, return early. */
+ lineart_destroy_render_data_keep_init(ld);
+ MEM_freeN(ld);
+ return false;
+ }
+
+ /* The exact same process as in MOD_lineart_compute_feature_lines() until occlusion finishes.
+ */
+
+ lineart_main_bounding_area_make_initial(ld);
+ lineart_main_cull_triangles(ld, false);
+ lineart_main_cull_triangles(ld, true);
+ lineart_main_free_adjacent_data(ld);
+ lineart_main_perspective_division(ld);
+ lineart_main_discard_out_of_frame_edges(ld);
+ lineart_main_add_triangles(ld);
+ lineart_main_bounding_areas_connect_post(ld);
+ lineart_main_link_lines(ld);
+ lineart_main_occlusion_begin(ld);
+
+ /* Do shadow cast stuff then get generated vert/edge data. */
+ lineart_shadow_cast(ld, true, false);
+ bool any_generated = lineart_shadow_cast_generate_edges(ld, true, r_veln, r_eeln);
+
+ if (ld->conf.shadow_selection) {
+ memcpy(r_calculated_edges_eln_list, &ld->geom.line_buffer_pointers, sizeof(ListBase));
+ }
+
+ if (ld->conf.shadow_enclose_shapes) {
+ /* Need loaded data for re-projecting the 3rd time to get shape boundary against lit/shaded
+ * region. */
+ (*r_shadow_ld_if_reproject) = ld;
+ }
+ else {
+ lineart_destroy_render_data_keep_init(ld);
+ MEM_freeN(ld);
+ }
+
+ if (G.debug_value == 4000) {
+ double t_elapsed = PIL_check_seconds_timer() - t_start;
+ printf("Line art shadow stage 1 time: %f\n", t_elapsed);
+ }
+
+ return any_generated;
+}
+
+typedef struct LineartShadowFinalizeData {
+ LineartData *ld;
+ LineartVert *v;
+ LineartEdge *e;
+} LineartShadowFinalizeData;
+
+static void lineart_shadow_transform_task(void *__restrict userdata,
+ const int element_index,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ LineartShadowFinalizeData *data = (LineartShadowFinalizeData *)userdata;
+ LineartData *ld = data->ld;
+ LineartVert *v = &data->v[element_index];
+ mul_v4_m4v3_db(v->fbcoord, ld->conf.view_projection, v->gloc);
+}
+
+static void lineart_shadow_finalize_shadow_edges_task(
+ void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ LineartShadowFinalizeData *data = (LineartShadowFinalizeData *)userdata;
+ LineartData *ld = data->ld;
+ LineartEdge *e = data->e;
+
+ if (e[i].flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) {
+ LineartElementLinkNode *eln = lineart_find_matching_eln(
+ &ld->geom.vertex_buffer_pointers, e[i].edge_identifier & LRT_OBINDEX_HIGHER);
+ if (eln) {
+ int v1i = (((e[i].edge_identifier) >> 32) & LRT_OBINDEX_LOWER);
+ int v2i = (e[i].edge_identifier & LRT_OBINDEX_LOWER);
+ LineartVert *v = (LineartVert *)eln->pointer;
+ /* If the global position is close enough, use the original vertex to prevent flickering
+ * caused by very slim boundary condition in point_triangle_relation(). */
+ if (LRT_CLOSE_LOOSER_v3(e[i].v1->gloc, v[v1i].gloc)) {
+ e[i].v1 = &v[v1i];
+ }
+ if (LRT_CLOSE_LOOSER_v3(e[i].v2->gloc, v[v2i].gloc)) {
+ e[i].v2 = &v[v2i];
+ }
+ }
+ }
+}
+
+/* Shadow segments needs to be transformed to view-camera space, just like any other objects.
+ */
+void lineart_main_transform_and_add_shadow(LineartData *ld,
+ LineartElementLinkNode *veln,
+ LineartElementLinkNode *eeln)
+{
+
+ TaskParallelSettings transform_settings;
+ BLI_parallel_range_settings_defaults(&transform_settings);
+ /* Set the minimum amount of edges a thread has to process. */
+ transform_settings.min_iter_per_thread = 8192;
+
+ LineartShadowFinalizeData data = {0};
+ data.ld = ld;
+ data.v = (LineartVert *)veln->pointer;
+ data.e = (LineartEdge *)eeln->pointer;
+
+ BLI_task_parallel_range(
+ 0, veln->element_count, &data, lineart_shadow_transform_task, &transform_settings);
+ BLI_task_parallel_range(0,
+ eeln->element_count,
+ &data,
+ lineart_shadow_finalize_shadow_edges_task,
+ &transform_settings);
+ for (int i = 0; i < eeln->element_count; i++) {
+ lineart_add_edge_to_array(&ld->pending_edges, &data.e[i]);
+ }
+
+ BLI_addtail(&ld->geom.vertex_buffer_pointers, veln);
+ BLI_addtail(&ld->geom.line_buffer_pointers, eeln);
+}
+
+/* Does the 3rd stage reprojection, will not re-load objects because #shadow_ld is not deleted.
+ * Only re-projects view camera edges and check visibility in light camera, then we can determine
+ * whether an edge landed on a lit or shaded area. */
+void lineart_main_make_enclosed_shapes(LineartData *ld, LineartData *shadow_ld)
+{
+ double t_start;
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
+ if (shadow_ld || ld->conf.shadow_use_silhouette) {
+ lineart_shadow_cast(ld, false, shadow_ld ? true : false);
+ if (ld->conf.shadow_use_silhouette) {
+ lineart_shadow_register_silhouette(ld);
+ }
+ }
+
+ if (G.debug_value == 4000) {
+ double t_elapsed = PIL_check_seconds_timer() - t_start;
+ printf("Line art shadow stage 2 cast and silhouette time: %f\n", t_elapsed);
+ }
+
+ if (!shadow_ld) {
+ return;
+ }
+
+ ld->shadow_data_pool = &ld->render_data_pool;
+
+ if (shadow_ld->pending_edges.array) {
+ MEM_freeN(shadow_ld->pending_edges.array);
+ shadow_ld->pending_edges.array = NULL;
+ shadow_ld->pending_edges.next = shadow_ld->pending_edges.max = 0;
+ }
+
+ LineartElementLinkNode *shadow_veln, *shadow_eeln;
+
+ bool any_generated = lineart_shadow_cast_generate_edges(ld, false, &shadow_veln, &shadow_eeln);
+
+ if (!any_generated) {
+ return;
+ }
+
+ LineartVert *v = shadow_veln->pointer;
+ for (int i = 0; i < shadow_veln->element_count; i++) {
+ mul_v4_m4v3_db(v[i].fbcoord, shadow_ld->conf.view_projection, v[i].gloc);
+ if (shadow_ld->conf.cam_is_persp) {
+ mul_v3db_db(v[i].fbcoord, (1 / v[i].fbcoord[3]));
+ }
+ }
+
+ lineart_finalize_object_edge_array_reserve(&shadow_ld->pending_edges,
+ shadow_eeln->element_count);
+
+ LineartEdge *se = shadow_eeln->pointer;
+ for (int i = 0; i < shadow_eeln->element_count; i++) {
+ lineart_add_edge_to_array(&shadow_ld->pending_edges, &se[i]);
+ }
+
+ shadow_ld->scheduled_count = 0;
+
+ lineart_main_clear_linked_edges(shadow_ld);
+ lineart_main_link_lines(shadow_ld);
+ lineart_main_occlusion_begin(shadow_ld);
+
+ lineart_shadow_register_enclosed_shapes(ld, shadow_ld);
+
+ if (G.debug_value == 4000) {
+ double t_elapsed = PIL_check_seconds_timer() - t_start;
+ printf("Line art shadow stage 2 total time: %f\n", t_elapsed);
+ }
+}