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')
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/BKE_collection.h3
-rw-r--r--source/blender/blenkernel/BKE_global.h1
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_gpencil_modifier.h3
-rw-r--r--source/blender/blenkernel/CMakeLists.txt4
-rw-r--r--source/blender/blenkernel/intern/collection.c47
-rw-r--r--source/blender/blenkernel/intern/gpencil.c42
-rw-r--r--source/blender/blenkernel/intern/object_update.c10
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c12
-rw-r--r--source/blender/blenloader/intern/readfile.c1
-rw-r--r--source/blender/blenloader/intern/versioning_290.c10
-rw-r--r--source/blender/blenloader/intern/writefile.c1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc2
-rw-r--r--source/blender/draw/DRW_engine.h5
-rw-r--r--source/blender/editors/CMakeLists.txt5
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_lineart.c120
-rw-r--r--source/blender/editors/include/ED_gpencil.h1
-rw-r--r--source/blender/editors/include/ED_lineart.h693
-rw-r--r--source/blender/editors/lineart/CMakeLists.txt46
-rw-r--r--source/blender/editors/lineart/lineart_chain.c984
-rw-r--r--source/blender/editors/lineart/lineart_cpu.c4289
-rw-r--r--source/blender/editors/lineart/lineart_intern.h108
-rw-r--r--source/blender/editors/lineart/lineart_ops.c250
-rw-r--r--source/blender/editors/lineart/lineart_util.c233
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt4
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c4
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt4
-rw-r--r--source/blender/editors/object/object_add.c97
-rw-r--r--source/blender/editors/render/CMakeLists.txt4
-rw-r--r--source/blender/editors/render/render_update.c13
-rw-r--r--source/blender/editors/space_api/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_api/spacetypes.c8
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c35
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c10
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c5
-rw-r--r--source/blender/editors/util/CMakeLists.txt5
-rw-r--r--source/blender/editors/util/ed_util.c11
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt6
-rw-r--r--source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c484
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c4
-rw-r--r--source/blender/makesdna/DNA_collection_types.h16
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h54
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h70
-rw-r--r--source/blender/makesdna/DNA_material_types.h11
-rw-r--r--source/blender/makesdna/DNA_object_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_object_types.h31
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h12
-rw-r--r--source/blender/makesdna/DNA_scene_types.h58
-rw-r--r--source/blender/makesdna/DNA_space_types.h1
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt11
-rw-r--r--source/blender/makesrna/intern/rna_collection.c29
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c225
-rw-r--r--source/blender/makesrna/intern/rna_material.c79
-rw-r--r--source/blender/makesrna/intern/rna_object.c80
-rw-r--r--source/blender/makesrna/intern/rna_scene.c202
-rw-r--r--source/blender/makesrna/intern/rna_space.c3
-rw-r--r--source/blender/python/intern/CMakeLists.txt4
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c7
75 files changed, 8489 insertions, 23 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index f875a990d0a..8d18cf0ae9a 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -56,6 +56,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_layer_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_light_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_lightprobe_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_lineart_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_linestyle_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_listBase.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_mask_types.h
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index d15aebfe03d..d0fca5e3796 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -82,6 +82,8 @@ struct Collection *BKE_collection_master_add(void);
bool BKE_collection_has_object(struct Collection *collection, const struct Object *ob);
bool BKE_collection_has_object_recursive(struct Collection *collection, struct Object *ob);
+bool BKE_collection_has_object_recursive_instanced(struct Collection *collection,
+ struct Object *ob);
struct Collection *BKE_collection_object_find(struct Main *bmain,
struct Scene *scene,
struct Collection *collection,
@@ -123,6 +125,7 @@ bool BKE_collection_object_cyclic_check(struct Main *bmain,
/* Object list cache. */
struct ListBase BKE_collection_object_cache_get(struct Collection *collection);
+ListBase BKE_collection_object_cache_instanced_get(struct Collection *collection);
void BKE_collection_object_cache_free(struct Collection *collection);
struct Base *BKE_collection_or_layer_objects(const struct ViewLayer *view_layer,
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index d6b6ffd425e..b06e9978a83 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -77,6 +77,7 @@ typedef struct Global {
* * 1112: Disable new Cloth internal springs handling (09/2014).
* * 1234: Disable new dyntopo code fixing skinny faces generation (04/2015).
* * 3001: Enable additional Fluid modifier (Mantaflow) options (02/2020).
+ * * 4000: Line Art state output and debugging logs (07/2020).
* * 16384 and above: Reserved for python (add-ons) usage.
*/
short debug_value;
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 5cfdcf241d1..a0a3f30d6d8 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -212,6 +212,10 @@ void BKE_gpencil_layer_mask_sort(struct bGPdata *gpd, struct bGPDlayer *gpl);
void BKE_gpencil_layer_mask_sort_all(struct bGPdata *gpd);
void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames);
+struct bGPDlayer *BKE_gpencil_layer_get_by_name(struct bGPdata *gpd,
+ char *name,
+ int first_if_not_found);
+
/* Brush */
struct Material *BKE_gpencil_brush_material_get(struct Brush *brush);
void BKE_gpencil_brush_material_set(struct Brush *brush, struct Material *material);
@@ -231,6 +235,7 @@ struct Material *BKE_gpencil_object_material_new(struct Main *bmain,
int *r_index);
int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma);
+int BKE_gpencil_object_material_get_index_name(struct Object *ob, char *name);
struct Material *BKE_gpencil_object_material_from_brush_get(struct Object *ob,
struct Brush *brush);
diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h
index c066c161f46..c6406c8478c 100644
--- a/source/blender/blenkernel/BKE_gpencil_modifier.h
+++ b/source/blender/blenkernel/BKE_gpencil_modifier.h
@@ -206,7 +206,8 @@ typedef struct GpencilModifierTypeInfo {
* This function is optional.
*/
void (*updateDepsgraph)(struct GpencilModifierData *md,
- const struct ModifierUpdateDepsgraphContext *ctx);
+ const struct ModifierUpdateDepsgraphContext *ctx,
+ const int mode);
/**
* Should return true if the modifier needs to be recalculated on time
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 310ac6c4903..2aca43c6df7 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -679,6 +679,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_ALEMBIC)
list(APPEND INC
../io/alembic
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 28b91dcc8ce..4e4134c7c8f 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -122,7 +122,9 @@ static void collection_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
}
collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
BLI_listbase_clear(&collection_dst->object_cache);
+ BLI_listbase_clear(&collection_dst->object_cache_instanced);
BLI_listbase_clear(&collection_dst->gobject);
BLI_listbase_clear(&collection_dst->children);
@@ -214,8 +216,10 @@ static void collection_blend_write(BlendWriter *writer, ID *id, const void *id_a
if (collection->id.us > 0 || BLO_write_is_undo(writer)) {
/* Clean up, important in undo case to reduce false detection of changed data-blocks. */
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
collection->tag = 0;
BLI_listbase_clear(&collection->object_cache);
+ BLI_listbase_clear(&collection->object_cache_instanced);
BLI_listbase_clear(&collection->parents);
/* write LibData */
@@ -246,8 +250,10 @@ void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collect
BKE_previewimg_blend_read(reader, collection->preview);
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
collection->tag = 0;
BLI_listbase_clear(&collection->object_cache);
+ BLI_listbase_clear(&collection->object_cache_instanced);
BLI_listbase_clear(&collection->parents);
#ifdef USE_COLLECTION_COMPAT_28
@@ -773,7 +779,10 @@ const char *BKE_collection_ui_name_get(struct Collection *collection)
/** \name Object List Cache
* \{ */
-static void collection_object_cache_fill(ListBase *lb, Collection *collection, int parent_restrict)
+static void collection_object_cache_fill(ListBase *lb,
+ Collection *collection,
+ int parent_restrict,
+ bool with_instances)
{
int child_restrict = collection->flag | parent_restrict;
@@ -784,6 +793,10 @@ static void collection_object_cache_fill(ListBase *lb, Collection *collection, i
base = MEM_callocN(sizeof(Base), "Object Base");
base->object = cob->ob;
BLI_addtail(lb, base);
+ if (with_instances && cob->ob->instance_collection) {
+ collection_object_cache_fill(
+ lb, cob->ob->instance_collection, child_restrict, with_instances);
+ }
}
/* Only collection flags are checked here currently, object restrict flag is checked
@@ -798,7 +811,7 @@ static void collection_object_cache_fill(ListBase *lb, Collection *collection, i
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- collection_object_cache_fill(lb, child->collection, child_restrict);
+ collection_object_cache_fill(lb, child->collection, child_restrict, with_instances);
}
}
@@ -809,7 +822,7 @@ ListBase BKE_collection_object_cache_get(Collection *collection)
BLI_mutex_lock(&cache_lock);
if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) {
- collection_object_cache_fill(&collection->object_cache, collection, 0);
+ collection_object_cache_fill(&collection->object_cache, collection, 0, false);
collection->flag |= COLLECTION_HAS_OBJECT_CACHE;
}
BLI_mutex_unlock(&cache_lock);
@@ -818,11 +831,29 @@ ListBase BKE_collection_object_cache_get(Collection *collection)
return collection->object_cache;
}
+ListBase BKE_collection_object_cache_instanced_get(Collection *collection)
+{
+ if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE_INSTANCED)) {
+ static ThreadMutex cache_lock = BLI_MUTEX_INITIALIZER;
+
+ BLI_mutex_lock(&cache_lock);
+ if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE_INSTANCED)) {
+ collection_object_cache_fill(&collection->object_cache_instanced, collection, 0, true);
+ collection->flag |= COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
+ }
+ BLI_mutex_unlock(&cache_lock);
+ }
+
+ return collection->object_cache_instanced;
+}
+
static void collection_object_cache_free(Collection *collection)
{
/* Clear own cache an for all parents, since those are affected by changes as well. */
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
BLI_freelistN(&collection->object_cache);
+ BLI_freelistN(&collection->object_cache_instanced);
LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) {
collection_object_cache_free(parent->collection);
@@ -928,6 +959,16 @@ bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
return (BLI_findptr(&objects, ob, offsetof(Base, object)));
}
+bool BKE_collection_has_object_recursive_instanced(Collection *collection, Object *ob)
+{
+ if (ELEM(NULL, collection, ob)) {
+ return false;
+ }
+
+ const ListBase objects = BKE_collection_object_cache_instanced_get(collection);
+ return (BLI_findptr(&objects, ob, offsetof(Base, object)));
+}
+
static Collection *collection_next_find(Main *bmain, Scene *scene, Collection *collection)
{
if (scene && collection == scene->master_collection) {
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 00dcaad83db..5e12bf52328 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -1663,6 +1663,32 @@ bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd)
return NULL;
}
+bGPDlayer *BKE_gpencil_layer_get_by_name(bGPdata *gpd, char *name, int first_if_not_found)
+{
+ bGPDlayer *gpl;
+ int i = 0;
+
+ /* error checking */
+ if (ELEM(NULL, gpd, gpd->layers.first)) {
+ return NULL;
+ }
+
+ /* loop over layers until found (assume only one active) */
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ if (STREQ(name, gpl->info)) {
+ return gpl;
+ }
+ i++;
+ }
+
+ /* no such layer */
+ if (first_if_not_found) {
+ return gpd->layers.first;
+ }
+ return NULL;
+}
+
+/* set the active gp-layer */
/**
* Set active grease pencil layer.
* \param gpd: Grease pencil data-block
@@ -2421,6 +2447,22 @@ int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
return -1;
}
+int BKE_gpencil_object_material_get_index_name(Object *ob, char *name)
+{
+ short *totcol = BKE_object_material_len_p(ob);
+ Material *read_ma = NULL;
+ for (short i = 0; i < *totcol; i++) {
+ read_ma = BKE_object_material_get(ob, i + 1);
+ /* Material names are like "MAMaterial.001" */
+ if (STREQ(name, &read_ma->id.name[2])) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Create a default palette */
/**
* Create a default palette.
* \param bmain: Main pointer
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 69442b7646c..1e6a099040f 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -176,12 +176,14 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
CustomData_MeshMasks cddata_masks = scene->customdata_mask;
CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH);
- if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
- /* Make sure Freestyle edge/face marks appear in DM for render (see T40315). */
+ /* Make sure Freestyle edge/face marks appear in DM for render (see T40315). Due to Line Art
+ * impementation, edge marks should also be shown in viewport. */
#ifdef WITH_FREESTYLE
- cddata_masks.emask |= CD_MASK_FREESTYLE_EDGE;
- cddata_masks.pmask |= CD_MASK_FREESTYLE_FACE;
+ cddata_masks.emask |= CD_MASK_FREESTYLE_EDGE;
+ cddata_masks.pmask |= CD_MASK_FREESTYLE_FACE;
+ cddata_masks.vmask |= CD_MASK_MDEFORMVERT;
#endif
+ if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
/* Always compute UVs, vertex colors as orcos for render. */
cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index c862290b262..028ca31a059 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -117,6 +117,9 @@ MINLINE float sasqrt(float fac);
MINLINE float interpf(float a, float b, float t);
MINLINE double interpd(double a, double b, double t);
+MINLINE float ratiof(float min, float max, float pos);
+MINLINE double ratiod(double min, double max, double pos);
+
/* NOTE: Compilers will upcast all types smaller than int to int when performing arithmetic
* operation. */
MINLINE int square_s(short a);
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 39945960e68..6481fac5a14 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -180,6 +180,18 @@ MINLINE double interpd(double target, double origin, double fac)
return (fac * target) + (1.0f - fac) * origin;
}
+MINLINE float ratiof(float min, float max, float pos)
+{
+ float range = max - min;
+ return range == 0 ? 0 : ((pos - min) / range);
+}
+
+MINLINE double ratiod(double min, double max, double pos)
+{
+ double range = max - min;
+ return range == 0 ? 0 : ((pos - min) / range);
+}
+
/* used for zoom values*/
MINLINE float power_of_2(float val)
{
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index de7353d827a..353eb336c42 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -51,6 +51,7 @@
#include "DNA_genfile.h"
#include "DNA_key_types.h"
#include "DNA_layer_types.h"
+#include "DNA_lineart_types.h"
#include "DNA_node_types.h"
#include "DNA_packedFile_types.h"
#include "DNA_sdna_types.h"
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 8cf840f665b..fc10e316aa1 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1527,6 +1527,16 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
FOREACH_NODETREE_END;
+ if (!DNA_struct_find(fd->filesdna, "SceneLineArt")) {
+ LISTBASE_FOREACH (Scene *, sc, &bmain->scenes) {
+ sc->lineart.crease_threshold = DEG2RAD(140.0f);
+ sc->lineart.line_types |= LRT_EDGE_FLAG_ALL_TYPE;
+ sc->lineart.flags |= (LRT_ALLOW_DUPLI_OBJECTS | LRT_REMOVE_DOUBLES);
+ sc->lineart.angle_splitting_threshold = DEG2RAD(60.0f);
+ sc->lineart.chaining_geometry_threshold = 0.001f;
+ sc->lineart.chaining_image_threshold = 0.001f;
+ }
+ }
/* Default properties editors to auto outliner sync. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 6fbd4b77487..518bb589061 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -95,6 +95,7 @@
#include "DNA_collection_types.h"
#include "DNA_fileglobal_types.h"
+#include "DNA_lineart_types.h"
#include "DNA_genfile.h"
#include "DNA_sdna_types.h"
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 96b4da34347..3cc2ec02165 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -2085,7 +2085,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
if (mti->updateDepsgraph) {
DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
- mti->updateDepsgraph(md, &ctx);
+ mti->updateDepsgraph(md, &ctx, graph_->mode);
}
if (BKE_object_modifier_gpencil_use_time(object, md)) {
TimeSourceKey time_src_key;
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index 2d5b93f4272..da112565867 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -138,6 +138,11 @@ void DRW_draw_select_id(struct Depsgraph *depsgraph,
bool DRW_render_check_grease_pencil(struct Depsgraph *depsgraph);
void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph);
+/* Line Art calls */
+#ifdef WITH_LINEART
+void DRW_scene_lineart_freecache(struct Scene *sce);
+#endif
+
/* This is here because GPUViewport needs it */
struct DRWInstanceDataList *DRW_instance_data_list_create(void);
void DRW_instance_data_list_free(struct DRWInstanceDataList *idatalist);
diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt
index 092198cea86..fd54d531eeb 100644
--- a/source/blender/editors/CMakeLists.txt
+++ b/source/blender/editors/CMakeLists.txt
@@ -67,3 +67,8 @@ if(WITH_BLENDER)
endif()
add_subdirectory(datafiles)
+
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+ add_subdirectory(lineart)
+endif()
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 1e91edbb3c0..47ae90acb74 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -36,6 +36,7 @@ set(SRC
annotate_paint.c
drawgpencil.c
editaction_gpencil.c
+ gpencil_add_lineart.c
gpencil_add_monkey.c
gpencil_add_stroke.c
gpencil_armature.c
diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c
new file mode 100644
index 00000000000..71253635ea8
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_add_lineart.c
@@ -0,0 +1,120 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "ED_gpencil.h"
+
+/* Definition of the most important info from a color */
+typedef struct ColorTemplate {
+ const char *name;
+ float line[4];
+ float fill[4];
+} ColorTemplate;
+
+/* Add color an ensure duplications (matched by name) */
+static int gpencil_lineart_material(Main *bmain,
+ Object *ob,
+ const ColorTemplate *pct,
+ const bool fill)
+{
+ short *totcol = BKE_object_material_len_p(ob);
+ Material *ma = NULL;
+ for (short i = 0; i < *totcol; i++) {
+ ma = BKE_gpencil_material(ob, i + 1);
+ if (STREQ(ma->id.name, pct->name)) {
+ return i;
+ }
+ }
+
+ int idx;
+
+ /* create a new one */
+ ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx);
+
+ copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
+ srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba);
+
+ copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
+ srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba);
+
+ if (fill) {
+ ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+ }
+
+ return idx;
+}
+
+/* ***************************************************************** */
+/* Color Data */
+
+static const ColorTemplate gp_stroke_material_black = {
+ "Black",
+ {0.0f, 0.0f, 0.0f, 1.0f},
+ {0.0f, 0.0f, 0.0f, 0.0f},
+};
+
+/* ***************************************************************** */
+/* LineArt API */
+
+/* Add a Simple LineArt setup. */
+void ED_gpencil_create_lineart(bContext *C, Object *ob)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ /* create colors */
+ int color_black = gpencil_lineart_material(bmain, ob, &gp_stroke_material_black, false);
+
+ /* set first color as active and in brushes */
+ ob->actcol = color_black + 1;
+
+ /* layers */
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+
+ /* frames */
+ BKE_gpencil_frame_addnew(lines, CFRA);
+
+ /* update depsgraph */
+ /* To trigger modifier update, this is still needed although we don't have any strokes. */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+}
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 2fbf280475b..f3b5abb1072 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -250,6 +250,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y);
void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]);
+void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob);
/* ------------ Object Utilities ------------ */
struct Object *ED_gpencil_add_object(struct bContext *C,
diff --git a/source/blender/editors/include/ED_lineart.h b/source/blender/editors/include/ED_lineart.h
new file mode 100644
index 00000000000..41f3badb236
--- /dev/null
+++ b/source/blender/editors/include/ED_lineart.h
@@ -0,0 +1,693 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#pragma once
+
+#ifndef WITH_LINEART
+# error Lineart code included in non-Lineart-enabled build
+#endif
+
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_threads.h"
+
+#include "DNA_lineart_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include <math.h>
+#include <string.h>
+
+typedef struct LineartStaticMemPoolNode {
+ Link item;
+ size_t size;
+ size_t used_byte;
+ /* User memory starts here */
+} LineartStaticMemPoolNode;
+
+typedef struct LineartStaticMemPool {
+ ListBase pools;
+ SpinLock lock_mem;
+} LineartStaticMemPool;
+
+typedef struct LineartRenderTriangleAdjacent {
+ struct LineartRenderLine *rl[3];
+} LineartRenderTriangleAdjacent;
+
+typedef struct LineartRenderTriangle {
+ struct LineartRenderVert *v[3];
+
+ /* first culled in line list to use adjacent triangle info, then go through triangle list. */
+ double gn[3];
+
+ /* Material flag is removed to save space. */
+ unsigned char transparency_mask;
+ unsigned char flags; /* eLineartTriangleFlags */
+
+ /* Now only use single link list, because we don't need to go back in order. */
+ struct LinkNode *intersecting_verts;
+} LineartRenderTriangle;
+
+typedef struct LineartRenderTriangleThread {
+ struct LineartRenderTriangle base;
+ /** This variable is used to store per-thread triangle-line testing pair,
+ * also re-used to store triangle-triangle pair for intersection testing stage.
+ * Do not directly use LineartRenderTriangleThread, but use it as a pointer,
+ * the size of LineartRenderTriangle is dynamically allocated to contain set thread number of
+ * "testing" field, at least one thread is present, thus we always have at least testing[0].*/
+ struct LineartRenderLine *testing[127];
+} LineartRenderTriangleThread;
+
+typedef enum eLineArtElementNodeFlag {
+ LRT_ELEMENT_IS_ADDITIONAL = (1 << 0),
+ LRT_ELEMENT_BORDER_ONLY = (1 << 1),
+ LRT_ELEMENT_NO_INTERSECTION = (1 << 2),
+} eLineArtElementNodeFlag;
+
+typedef struct LineartRenderElementLinkNode {
+ struct LineartRenderElementLinkNode *next, *prev;
+ void *pointer;
+ int element_count;
+ void *object_ref;
+ eLineArtElementNodeFlag flags;
+
+ /* Per object value, always set, if not enabled by ObjectLineArt, then it's set to global. */
+ float crease_threshold;
+} LineartRenderElementLinkNode;
+
+typedef struct LineartRenderLineSegment {
+ struct LineartRenderLineSegment *next, *prev;
+ /** at==0: left at==1: right (this is in 2D projected space) */
+ double at;
+ /** Occlusion level after "at" point */
+ unsigned char occlusion;
+
+ /** For determining lines beind a glass window material.
+ * the size of this variable should also be dynamically decided, 1 byte to 8 byte,
+ * allows 8 to 64 materials for "transparent mask". 1 byte (8 materials) should be
+ * enought for most cases.
+ */
+ unsigned char transparency_mask;
+} LineartRenderLineSegment;
+
+typedef struct LineartRenderVert {
+ double gloc[3];
+ double fbcoord[4];
+
+ 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.
+ */
+ char flag;
+
+} LineartRenderVert;
+
+typedef struct LineartRenderVertIntersection {
+ struct LineartRenderVert base;
+ /* Use vert index because we only use this to check vertex equal. This way we save 8 Bytes. */
+ int isec1, isec2;
+ struct LineartRenderTriangle *intersecting_with;
+} LineartRenderVertIntersection;
+
+typedef enum eLineArtVertFlags {
+ LRT_VERT_HAS_INTERSECTION_DATA = (1 << 0),
+ LRT_VERT_EDGE_USED = (1 << 1),
+} eLineArtVertFlags;
+
+typedef struct LineartRenderLine {
+ /* We only need link node kind of list here. */
+ struct LineartRenderLine *next;
+ struct LineartRenderVert *l, *r;
+ /* Local vertex index for two ends, not puting in RenderVert because all verts are loaded, so as
+ * long as fewer than half of the mesh edges are becoming a feature line, we save more memory. */
+ int l_obindex, r_obindex;
+ struct LineartRenderTriangle *tl, *tr;
+ ListBase segments;
+ char min_occ;
+
+ /** Also for line type determination on chainning */
+ unsigned char flags;
+
+ /** Still need this entry because culled lines will not add to object reln node,
+ * TODO: If really need more savings, we can allocate this in a "extended" way too, but we need
+ * another bit in flags to be able to show the difference.
+ */
+ struct Object *object_ref;
+} LineartRenderLine;
+
+typedef struct LineartRenderLineChain {
+ struct LineartRenderLineChain *next, *prev;
+ ListBase chain;
+
+ /** Calculated before draw cmd. */
+ float length;
+
+ /** Used when re-connecting and gp stroke generation */
+ char picked;
+ char level;
+
+ /** Chain now only contains one type of segments */
+ int type;
+ unsigned char transparency_mask;
+
+ struct Object *object_ref;
+} LineartRenderLineChain;
+
+typedef struct LineartRenderLineChainItem {
+ struct LineartRenderLineChainItem *next, *prev;
+ /** Need z value for fading */
+ float pos[3];
+ /** For restoring position to 3d space */
+ float gpos[3];
+ float normal[3];
+ char line_type;
+ char occlusion;
+ unsigned char transparency_mask;
+ size_t index;
+} LineartRenderLineChainItem;
+
+typedef struct LineartChainRegisterEntry {
+ struct LineartChainRegisterEntry *next, *prev;
+ LineartRenderLineChain *rlc;
+ LineartRenderLineChainItem *rlci;
+ char picked;
+
+ /** left/right mark.
+ * Because we revert list in chaining so we need the flag. */
+ char is_left;
+} LineartChainRegisterEntry;
+
+typedef struct LineartRenderBuffer {
+ struct LineartRenderBuffer *prev, *next;
+
+ /** For render. */
+ int is_copied;
+
+ int w, h;
+ int tile_size_w, tile_size_h;
+ int tile_count_x, tile_count_y;
+ double width_per_tile, height_per_tile;
+ double view_projection[4][4];
+
+ int output_mode;
+ int output_aa_level;
+
+ struct LineartBoundingArea *initial_bounding_areas;
+ unsigned int bounding_area_count;
+
+ ListBase vertex_buffer_pointers;
+ ListBase line_buffer_pointers;
+ ListBase triangle_buffer_pointers;
+
+ /* This one's memory is not from main pool and is free()ed after culling stage. */
+ ListBase triangle_adjacent_pointers;
+
+ ListBase intersecting_vertex_buffer;
+ /** Use the one comes with Line Art. */
+ LineartStaticMemPool render_data_pool;
+ ListBase wasted_cuts;
+ SpinLock lock_cuts;
+
+ struct Material *material_pointers[2048];
+
+ /* Render status */
+ double view_vector[3];
+
+ int triangle_size;
+
+ unsigned int contour_count;
+ unsigned int contour_processed;
+ LineartRenderLine *contour_managed;
+ /* Now changed to linknodes. */
+ LineartRenderLine *contours;
+
+ unsigned int intersection_count;
+ unsigned int intersection_processed;
+ LineartRenderLine *intersection_managed;
+ LineartRenderLine *intersection_lines;
+
+ unsigned int crease_count;
+ unsigned int crease_processed;
+ LineartRenderLine *crease_managed;
+ LineartRenderLine *crease_lines;
+
+ unsigned int material_line_count;
+ unsigned int material_processed;
+ LineartRenderLine *material_managed;
+ LineartRenderLine *material_lines;
+
+ unsigned int edge_mark_count;
+ unsigned int edge_mark_processed;
+ LineartRenderLine *edge_mark_managed;
+ LineartRenderLine *edge_marks;
+
+ ListBase chains;
+
+ /** For managing calculation tasks for multiple threads. */
+ SpinLock lock_task;
+
+ /* settings */
+
+ int max_occlusion_level;
+ double crease_angle;
+ double crease_cos;
+ int thread_count;
+
+ int draw_material_preview;
+ double material_transparency;
+
+ bool use_contour;
+ bool use_crease;
+ bool use_material;
+ bool use_edge_marks;
+ bool use_intersections;
+ bool fuzzy_intersections;
+ bool fuzzy_everything;
+ bool allow_boundaries;
+ bool remove_doubles;
+
+ /** Keep an copy of these data so the scene can be freed when lineart is runnning. */
+ bool cam_is_persp;
+ float cam_obmat[4][4];
+ double camera_pos[3];
+ double near_clip, far_clip;
+ float shift_x, shift_y;
+ float crease_threshold;
+ float chaining_image_threshold;
+ float chaining_geometry_threshold;
+ float angle_splitting_threshold;
+} LineartRenderBuffer;
+
+typedef enum eLineartRenderStatus {
+ LRT_RENDER_IDLE = 0,
+ LRT_RENDER_RUNNING = 1,
+ LRT_RENDER_INCOMPELTE = 2, /* Not used yet. */
+ LRT_RENDER_FINISHED = 3,
+ LRT_RENDER_CANCELING = 4,
+} eLineartRenderStatus;
+
+typedef enum eLineartInitStatus {
+ LRT_INIT_ENGINE = (1 << 0),
+ LRT_INIT_LOCKS = (1 << 1),
+} eLineartInitStatus;
+
+typedef enum eLineartModifierSyncStatus {
+ LRT_SYNC_IDLE = 0,
+ LRT_SYNC_WAITING = 1,
+ LRT_SYNC_FRESH = 2,
+ LRT_SYNC_IGNORE = 3,
+ LRT_SYNC_CLEARING = 4,
+} eLineartModifierSyncStatus;
+
+typedef struct LineartSharedResource {
+
+ /* We only allocate once for all */
+ LineartRenderBuffer *render_buffer_shared;
+
+ /* Don't put this in render buffer as the checker function doesn't have rb pointer, this design
+ * is for performance. */
+ char allow_overlapping_edges;
+
+ /* cache */
+ struct BLI_mempool *mp_sample;
+ struct BLI_mempool *mp_line_strip;
+ struct BLI_mempool *mp_line_strip_point;
+ struct BLI_mempool *mp_batch_list;
+
+ struct TaskPool *background_render_task;
+ struct TaskPool *pending_render_task;
+
+ eLineartInitStatus init_complete;
+
+ /** To bypass or cancel rendering.
+ * This status flag should be kept in lineart_share not render_buffer,
+ * because render_buffer will get re-initialized every frame.
+ */
+ SpinLock lock_render_status;
+ eLineartRenderStatus flag_render_status;
+ eLineartModifierSyncStatus flag_sync_staus;
+ /** count of pending modifiers that is waiting for the data. */
+ int customers;
+
+ int thread_count;
+
+ /** To determine whether all threads are completely canceled. Each thread add 1 into this value
+ * before return, until it reaches thread count. Not needed for the implementation at the moment
+ * as occlusion thread is work-and-wait, preserved for future usages. */
+ int canceled_thread_accumulator;
+
+ /** Geometry loading is done in the worker thread,
+ * Lock the render thread until loading is done, so that
+ * we can avoid depsgrapgh deleting the scene before
+ * LRT finishes loading. Also keep this in lineart_share.
+ */
+ SpinLock lock_loader;
+
+ /** When drawing in the viewport, use the following values. */
+ /** Set to override to -1 before creating lineart render buffer to use scene camera. */
+ int viewport_camera_override;
+ char camera_is_persp;
+ float camera_pos[3];
+ float near_clip, far_clip;
+ float viewinv[4][4];
+ float persp[4][4];
+ float viewquat[4];
+
+ /* Use these to set cursor and progress. */
+ wmWindowManager *wm;
+ wmWindow *main_window;
+} LineartSharedResource;
+
+#define DBL_TRIANGLE_LIM 1e-8
+#define DBL_EDGE_LIM 1e-9
+
+#define LRT_MEMORY_POOL_64MB (1 << 26)
+
+typedef enum eLineartTriangleFlags {
+ LRT_CULL_DONT_CARE = 0,
+ LRT_CULL_USED = (1 << 0),
+ LRT_CULL_DISCARD = (1 << 1),
+ LRT_CULL_GENERATED = (1 << 2),
+ LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3),
+ LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
+} eLineartTriangleFlags;
+
+/** Controls how many lines a worker thread is processing at one request.
+ * There's no significant performance impact on choosing different values.
+ * Don't make it too small so that the worker thread won't request too many times. */
+#define LRT_THREAD_LINE_COUNT 1000
+
+typedef struct LineartRenderTaskInfo {
+ int thread_id;
+
+ LineartRenderLine *contour;
+ LineartRenderLine *contour_end;
+
+ LineartRenderLine *intersection;
+ LineartRenderLine *intersection_end;
+
+ LineartRenderLine *crease;
+ LineartRenderLine *crease_end;
+
+ LineartRenderLine *material;
+ LineartRenderLine *material_end;
+
+ LineartRenderLine *edge_mark;
+ LineartRenderLine *edge_mark_end;
+
+} LineartRenderTaskInfo;
+
+/** Bounding area diagram:
+ *
+ * +----+ <----U (Upper edge Y value)
+ * | |
+ * +----+ <----B (Bottom edge Y value)
+ * ^ ^
+ * L R (Left/Right edge X value)
+ *
+ * Example structure when subdividing 1 bounding areas:
+ * 1 area can be divided into 4 smaller children to
+ * accomodate image areas with denser triangle distribution.
+ * +--+--+-----+
+ * +--+--+ |
+ * +--+--+-----+
+ * | | |
+ * +-----+-----+
+ * lp/rp/up/bp is the list for
+ * storing pointers to adjacent bounding areas.
+ */
+typedef struct LineartBoundingArea {
+ double l, r, u, b;
+ double cx, cy;
+
+ /** 1,2,3,4 quadrant */
+ struct LineartBoundingArea *child;
+
+ ListBase lp;
+ ListBase rp;
+ ListBase up;
+ ListBase bp;
+
+ short triangle_count;
+
+ ListBase linked_triangles;
+ ListBase linked_lines;
+
+ /** Reserved for image space reduction && multithread chainning */
+ ListBase linked_chains;
+} LineartBoundingArea;
+
+#define LRT_TILE(tile, r, c, CCount) tile[r * CCount + c]
+
+#define LRT_CLAMP(a, Min, Max) a = a < Min ? Min : (a > Max ? Max : a)
+
+#define LRT_MAX3_INDEX(a, b, c) (a > b ? (a > c ? 0 : (b > c ? 1 : 2)) : (b > c ? 1 : 2))
+
+#define LRT_MIN3_INDEX(a, b, c) (a < b ? (a < c ? 0 : (b < c ? 1 : 2)) : (b < c ? 1 : 2))
+
+#define LRT_MAX3_INDEX_ABC(x, y, z) (x > y ? (x > z ? a : (y > z ? b : c)) : (y > z ? b : c))
+
+#define LRT_MIN3_INDEX_ABC(x, y, z) (x < y ? (x < z ? a : (y < z ? b : c)) : (y < z ? b : c))
+
+#define LRT_ABC(index) (index == 0 ? a : (index == 1 ? b : c))
+
+#define LRT_DOUBLE_CLOSE_ENOUGH(a, b) (((a) + DBL_EDGE_LIM) >= (b) && ((a)-DBL_EDGE_LIM) <= (b))
+
+BLI_INLINE int lineart_LineIntersectTest2d(
+ const double *a1, const double *a2, const double *b1, const double *b2, double *aRatio)
+{
+#define USE_VECTOR_LINE_INTERSECTION
+#ifdef USE_VECTOR_LINE_INTERSECTION
+
+ /* 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;
+ double rr;
+
+ if (fabs(a2[0] - a1[0]) > fabs(a2[1] - a1[1])) {
+ *aRatio = ratiod(a1[0], a2[0], rx);
+ if (fabs(b2[0] - b1[0]) > fabs(b2[1] - b1[1])) {
+ rr = ratiod(b1[0], b2[0], rx);
+ }
+ else {
+ rr = ratiod(b1[1], b2[1], ry);
+ }
+ if ((*aRatio) > 0 && (*aRatio) < 1 && rr > 0 && rr < 1) {
+ return 1;
+ }
+ return 0;
+ }
+ else {
+ *aRatio = ratiod(a1[1], a2[1], ry);
+ if (fabs(b2[0] - b1[0]) > fabs(b2[1] - b1[1])) {
+ rr = ratiod(b1[0], b2[0], rx);
+ }
+ else {
+ rr = ratiod(b1[1], b2[1], ry);
+ }
+ if ((*aRatio) > 0 && (*aRatio) < 1 && rr > 0 && rr < 1) {
+ 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)) {
+ *aRatio = 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);
+ *aRatio = 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);
+ *aRatio = 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;
+
+ *aRatio = ratio;
+ }
+ }
+
+ if (LRT_DOUBLE_CLOSE_ENOUGH(b1[0], b2[0])) {
+ y = interpd(a2[1], a1[1], ratio);
+ if (y > MAX2(b1[1], b2[1]) || y < MIN2(b1[1], b2[1]))
+ return 0;
+ }
+ else if (ratio <= 0 || ratio > 1 || (b1[0] > b2[0] && x > b1[0]) ||
+ (b1[0] < b2[0] && x < b1[0]) || (b2[0] > b1[0] && x > b2[0]) ||
+ (b2[0] < b1[0] && x < b2[0]))
+ return 0;
+
+ return 1;
+#endif
+}
+
+int ED_lineart_point_inside_triangled(double v[2], double v0[2], double v1[2], double v2[2]);
+
+struct Depsgraph;
+struct SceneLineArt;
+struct Scene;
+struct LineartRenderBuffer;
+
+void ED_lineart_init_locks(void);
+struct LineartRenderBuffer *ED_lineart_create_render_buffer(struct Scene *s);
+void ED_lineart_destroy_render_data(void);
+void ED_lineart_destroy_render_data_external(void);
+
+int ED_lineart_object_collection_usage_check(struct Collection *c, struct Object *o);
+
+void ED_lineart_chain_feature_lines(LineartRenderBuffer *rb);
+void ED_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb);
+void ED_lineart_chain_connect(LineartRenderBuffer *rb, const int do_geometry_space);
+void ED_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
+void ED_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
+
+int ED_lineart_chain_count(const LineartRenderLineChain *rlc);
+void ED_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb);
+
+void ED_lineart_calculation_flag_set(eLineartRenderStatus flag);
+bool ED_lineart_calculation_flag_check(eLineartRenderStatus flag);
+
+void ED_lineart_modifier_sync_flag_set(eLineartModifierSyncStatus flag, bool is_from_modifier);
+bool ED_lineart_modifier_sync_flag_check(eLineartModifierSyncStatus flag);
+void ED_lineart_modifier_sync_add_customer(void);
+void ED_lineart_modifier_sync_remove_customer(void);
+bool ED_lineart_modifier_sync_still_has_customer(void);
+
+int ED_lineart_compute_feature_lines_internal(struct Depsgraph *depsgraph,
+ const int show_frame_progress);
+
+void ED_lineart_compute_feature_lines_background(struct Depsgraph *dg,
+ const int show_frame_progress);
+
+struct Scene;
+
+LineartBoundingArea *ED_lineart_get_point_bounding_area(LineartRenderBuffer *rb,
+ double x,
+ double y);
+
+LineartBoundingArea *ED_lineart_get_point_bounding_area_deep(LineartRenderBuffer *rb,
+ double x,
+ double y);
+
+struct bGPDlayer;
+struct bGPDframe;
+struct GpencilModifierData;
+
+void ED_lineart_gpencil_generate(struct Depsgraph *depsgraph,
+ Object *gpencil_object,
+ float (*gp_obmat_inverse)[4],
+ struct bGPDlayer *UNUSED(gpl),
+ struct bGPDframe *gpf,
+ int level_start,
+ int level_end,
+ int material_nr,
+ struct Object *source_object,
+ struct Collection *source_collection,
+ int types,
+ unsigned char transparency_flags,
+ unsigned char transparency_mask,
+ short thickness,
+ float opacity,
+ float pre_sample_length,
+ const char *source_vgname,
+ const char *vgname,
+ int modifier_flags);
+
+void ED_lineart_gpencil_generate_with_type(struct Depsgraph *depsgraph,
+ struct Object *ob,
+ struct bGPDlayer *gpl,
+ struct bGPDframe *gpf,
+ char source_type,
+ void *source_reference,
+ int level_start,
+ int level_end,
+ int mat_nr,
+ short line_types,
+ unsigned char transparency_flags,
+ unsigned char transparency_mask,
+ short thickness,
+ float opacity,
+ float pre_sample_length,
+ const char *source_vgname,
+ const char *vgname,
+ int modifier_flags);
+
+struct bContext;
+
+void ED_lineart_post_frame_update_external(struct bContext *C,
+ struct Scene *s,
+ struct Depsgraph *dg,
+ bool from_modifier);
+
+struct SceneLineArt;
+
+void ED_lineart_update_render_progress(int nr, const char *info);
+
+float ED_lineart_chain_compute_length(LineartRenderLineChain *rlc);
+
+struct wmOperatorType;
+
+/* Operator types */
+void SCENE_OT_lineart_update_strokes(struct wmOperatorType *ot);
+void SCENE_OT_lineart_bake_strokes(struct wmOperatorType *ot);
+
+void ED_operatortypes_lineart(void);
diff --git a/source/blender/editors/lineart/CMakeLists.txt b/source/blender/editors/lineart/CMakeLists.txt
new file mode 100644
index 00000000000..d8c5ab1d77f
--- /dev/null
+++ b/source/blender/editors/lineart/CMakeLists.txt
@@ -0,0 +1,46 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ ../include
+ ../../blenkernel
+ ../../blenlib
+ ../../bmesh
+ ../../depsgraph
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+ lineart_ops.c
+ lineart_cpu.c
+ lineart_chain.c
+ lineart_util.c
+
+ lineart_intern.h
+)
+
+set(LIB
+)
+
+blender_add_lib(bf_editor_lineart "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/lineart/lineart_chain.c b/source/blender/editors/lineart/lineart_chain.c
new file mode 100644
index 00000000000..08297176e13
--- /dev/null
+++ b/source/blender/editors/lineart/lineart_chain.c
@@ -0,0 +1,984 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_customdata.h"
+#include "BKE_object.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_lineart_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_scene_types.h"
+
+#include "ED_lineart.h"
+
+#include "bmesh.h"
+
+#include "lineart_intern.h"
+
+#include <math.h>
+
+#define LRT_OTHER_RV(rl, rv) ((rv) == (rl)->l ? (rl)->r : (rl)->l)
+
+static LineartRenderLine *lineart_line_get_connected(LineartBoundingArea *ba,
+ LineartRenderVert *rv,
+ LineartRenderVert **new_rv,
+ int match_flag)
+{
+ LineartRenderLine *nrl;
+
+ LISTBASE_FOREACH (LinkData *, lip, &ba->linked_lines) {
+ nrl = lip->data;
+
+ if ((!(nrl->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (nrl->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) {
+ continue;
+ }
+
+ if (match_flag && ((nrl->flags & LRT_EDGE_FLAG_ALL_TYPE) & match_flag) == 0) {
+ continue;
+ }
+
+ /* always chain connected lines for now. */
+ /* simplification will take care of the sharp points. */
+
+ if (rv != nrl->l && rv != nrl->r) {
+ if (nrl->flags & LRT_EDGE_FLAG_INTERSECTION) {
+ if (rv->fbcoord[0] == nrl->l->fbcoord[0] && rv->fbcoord[1] == nrl->l->fbcoord[1]) {
+ *new_rv = LRT_OTHER_RV(nrl, nrl->l);
+ return nrl;
+ }
+ else {
+ if (rv->fbcoord[0] == nrl->r->fbcoord[0] && rv->fbcoord[1] == nrl->r->fbcoord[1]) {
+ *new_rv = LRT_OTHER_RV(nrl, nrl->r);
+ return nrl;
+ }
+ }
+ }
+ continue;
+ }
+
+ *new_rv = LRT_OTHER_RV(nrl, rv);
+ return nrl;
+ }
+
+ return 0;
+}
+
+static LineartRenderLineChain *lineart_chain_create(LineartRenderBuffer *rb)
+{
+ LineartRenderLineChain *rlc;
+ rlc = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLineChain));
+
+ BLI_addtail(&rb->chains, rlc);
+
+ return rlc;
+}
+
+static bool lineart_point_overlapping(LineartRenderLineChainItem *rlci,
+ float x,
+ float y,
+ double threshold)
+{
+ if (!rlci) {
+ return false;
+ }
+ if (((rlci->pos[0] + threshold) >= x) && ((rlci->pos[0] - threshold) <= x) &&
+ ((rlci->pos[1] + threshold) >= y) && ((rlci->pos[1] - threshold) <= y)) {
+ return true;
+ }
+ return false;
+}
+
+static LineartRenderLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
+ LineartRenderLineChain *rlc,
+ float *fbcoord,
+ float *gpos,
+ float *normal,
+ char type,
+ int level,
+ unsigned char transparency_mask,
+ size_t index)
+{
+ LineartRenderLineChainItem *rlci;
+
+ if (lineart_point_overlapping(rlc->chain.last, fbcoord[0], fbcoord[1], 1e-5)) {
+ /* Because segment type is determined by the leading chain point, so we need to ensure the
+ * type and occlusion is correct after omitting overlapping point*/
+ LineartRenderLineChainItem *old_rlci = rlc->chain.last;
+ old_rlci->line_type = type;
+ old_rlci->occlusion = level;
+ return old_rlci;
+ }
+
+ rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLineChainItem));
+
+ copy_v2_v2(rlci->pos, fbcoord);
+ copy_v3_v3(rlci->gpos, gpos);
+ rlci->index = index;
+ copy_v3_v3(rlci->normal, normal);
+ rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
+ rlci->occlusion = level;
+ rlci->transparency_mask = transparency_mask;
+ BLI_addtail(&rlc->chain, rlci);
+
+ return rlci;
+}
+
+static LineartRenderLineChainItem *lineart_chain_push_point(LineartRenderBuffer *rb,
+ LineartRenderLineChain *rlc,
+ float *fbcoord,
+ float *gpos,
+ float *normal,
+ char type,
+ int level,
+ unsigned char transparency_mask,
+ size_t index)
+{
+ LineartRenderLineChainItem *rlci;
+
+ if (lineart_point_overlapping(rlc->chain.first, fbcoord[0], fbcoord[1], 1e-5)) {
+ return rlc->chain.first;
+ }
+
+ rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLineChainItem));
+
+ copy_v2_v2(rlci->pos, fbcoord);
+ copy_v3_v3(rlci->gpos, gpos);
+ rlci->index = index;
+ copy_v3_v3(rlci->normal, normal);
+ rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
+ rlci->occlusion = level;
+ rlci->transparency_mask = transparency_mask;
+ BLI_addhead(&rlc->chain, rlci);
+
+ return rlci;
+}
+
+void ED_lineart_chain_feature_lines(LineartRenderBuffer *rb)
+{
+ LineartRenderLineChain *rlc;
+ LineartRenderLineChainItem *rlci;
+ LineartBoundingArea *ba;
+ LineartRenderLineSegment *rls;
+ int last_occlusion;
+ unsigned char last_transparency;
+ /* for converting from double */
+ float use_fbcoord[2];
+ float use_gpos[3];
+
+#define VERT_COORD_TO_FLOAT(a) \
+ copy_v2fl_v2db(use_fbcoord, (a)->fbcoord); \
+ copy_v3fl_v3db(use_gpos, (a)->gloc);
+
+#define POS_TO_FLOAT(lpos, gpos) \
+ copy_v2fl_v2db(use_fbcoord, lpos); \
+ copy_v3fl_v3db(use_gpos, gpos);
+
+ LRT_ITER_ALL_LINES_BEGIN
+ {
+
+ if ((!(rl->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (rl->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) {
+ continue;
+ }
+
+ rl->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
+
+ rlc = lineart_chain_create(rb);
+
+ rlc->object_ref = rl->object_ref; /* can only be the same object in a chain. */
+
+ LineartRenderLine *new_rl = rl;
+ LineartRenderVert *new_rv;
+ float N[3] = {0};
+
+ if (rl->tl) {
+ N[0] += rl->tl->gn[0];
+ N[1] += rl->tl->gn[1];
+ N[2] += rl->tl->gn[2];
+ }
+ if (rl->tr) {
+ N[0] += rl->tr->gn[0];
+ N[1] += rl->tr->gn[1];
+ N[2] += rl->tr->gn[2];
+ }
+ if (rl->tl || rl->tr) {
+ normalize_v3(N);
+ }
+
+ /* step 1: grow left. */
+ ba = ED_lineart_get_point_bounding_area_deep(rb, rl->l->fbcoord[0], rl->l->fbcoord[1]);
+ new_rv = rl->l;
+ rls = rl->segments.first;
+ VERT_COORD_TO_FLOAT(new_rv);
+ lineart_chain_push_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ rl->flags,
+ rls->occlusion,
+ rls->transparency_mask,
+ rl->l_obindex);
+ while (ba && (new_rl = lineart_line_get_connected(ba, new_rv, &new_rv, rl->flags))) {
+ new_rl->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
+
+ if (new_rl->tl || new_rl->tr) {
+ zero_v3(N);
+ if (new_rl->tl) {
+ N[0] += new_rl->tl->gn[0];
+ N[1] += new_rl->tl->gn[1];
+ N[2] += new_rl->tl->gn[2];
+ }
+ if (new_rl->tr) {
+ N[0] += new_rl->tr->gn[0];
+ N[1] += new_rl->tr->gn[1];
+ N[2] += new_rl->tr->gn[2];
+ }
+ normalize_v3(N);
+ }
+
+ if (new_rv == new_rl->l) {
+ for (rls = new_rl->segments.last; rls; rls = rls->prev) {
+ double gpos[3], lpos[3];
+ double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
+ double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
+ interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
+ POS_TO_FLOAT(lpos, gpos)
+ lineart_chain_push_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ new_rl->flags,
+ rls->occlusion,
+ rls->transparency_mask,
+ new_rl->l_obindex);
+ last_occlusion = rls->occlusion;
+ last_transparency = rls->transparency_mask;
+ }
+ }
+ else if (new_rv == new_rl->r) {
+ rls = new_rl->segments.first;
+ last_occlusion = rls->occlusion;
+ last_transparency = rls->transparency_mask;
+ rls = rls->next;
+ for (; rls; rls = rls->next) {
+ double gpos[3], lpos[3];
+ double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
+ double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
+ interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
+ POS_TO_FLOAT(lpos, gpos)
+ lineart_chain_push_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ new_rl->flags,
+ last_occlusion,
+ last_transparency,
+ new_rl->r_obindex);
+ last_occlusion = rls->occlusion;
+ last_transparency = rls->transparency_mask;
+ }
+ VERT_COORD_TO_FLOAT(new_rl->r);
+ lineart_chain_push_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ new_rl->flags,
+ last_occlusion,
+ last_transparency,
+ new_rl->r_obindex);
+ }
+ ba = ED_lineart_get_point_bounding_area_deep(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ }
+
+ /* Restore normal value. */
+ if (rl->tl || rl->tr) {
+ zero_v3(N);
+ if (rl->tl) {
+ N[0] += rl->tl->gn[0];
+ N[1] += rl->tl->gn[1];
+ N[2] += rl->tl->gn[2];
+ }
+ if (rl->tr) {
+ N[0] += rl->tr->gn[0];
+ N[1] += rl->tr->gn[1];
+ N[2] += rl->tr->gn[2];
+ }
+ normalize_v3(N);
+ }
+ /* step 2: this line. */
+ rls = rl->segments.first;
+ last_occlusion = ((LineartRenderLineSegment *)rls)->occlusion;
+ last_transparency = ((LineartRenderLineSegment *)rls)->transparency_mask;
+ for (rls = rls->next; rls; rls = rls->next) {
+ double gpos[3], lpos[3];
+ double *lfb = rl->l->fbcoord, *rfb = rl->r->fbcoord;
+ double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, rl->l->fbcoord, rl->r->fbcoord, rls->at);
+ interp_v3_v3v3_db(gpos, rl->l->gloc, rl->r->gloc, global_at);
+ POS_TO_FLOAT(lpos, gpos)
+ lineart_chain_append_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ rl->flags,
+ rls->occlusion,
+ rls->transparency_mask,
+ rl->l_obindex);
+ last_occlusion = rls->occlusion;
+ }
+ VERT_COORD_TO_FLOAT(rl->r)
+ lineart_chain_append_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ rl->flags,
+ last_occlusion,
+ last_transparency,
+ rl->r_obindex);
+
+ /* step 3: grow right. */
+ ba = ED_lineart_get_point_bounding_area_deep(rb, rl->r->fbcoord[0], rl->r->fbcoord[1]);
+ new_rv = rl->r;
+ /* below already done in step 2. */
+ /* lineart_chain_push_point(rb,rlc,new_rv->fbcoord[0],new_rv->fbcoord[1],rl->flags,0);
+ . */
+ while (ba && (new_rl = lineart_line_get_connected(ba, new_rv, &new_rv, rl->flags))) {
+ new_rl->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
+
+ if (new_rl->tl || new_rl->tr) {
+ zero_v3(N);
+ if (new_rl->tl) {
+ N[0] += new_rl->tl->gn[0];
+ N[1] += new_rl->tl->gn[1];
+ N[2] += new_rl->tl->gn[2];
+ }
+ if (new_rl->tr) {
+ N[0] += new_rl->tr->gn[0];
+ N[1] += new_rl->tr->gn[1];
+ N[2] += new_rl->tr->gn[2];
+ }
+ normalize_v3(N);
+ }
+
+ /* fix leading vertex type. */
+ rlci = rlc->chain.last;
+ rlci->line_type = new_rl->flags & LRT_EDGE_FLAG_ALL_TYPE;
+
+ if (new_rv == new_rl->l) {
+ rls = new_rl->segments.last;
+ last_occlusion = rls->occlusion;
+ last_transparency = rls->transparency_mask;
+ rlci->occlusion = last_occlusion; /* fix leading vertex occlusion. */
+ for (rls = new_rl->segments.last; rls; rls = rls->prev) {
+ double gpos[3], lpos[3];
+ double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
+ double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
+ interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
+ last_occlusion = rls->prev ? rls->prev->occlusion : last_occlusion;
+ POS_TO_FLOAT(lpos, gpos)
+ lineart_chain_append_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ new_rl->flags,
+ last_occlusion,
+ last_transparency,
+ new_rl->l_obindex);
+ }
+ }
+ else if (new_rv == new_rl->r) {
+ rls = new_rl->segments.first;
+ last_occlusion = rls->occlusion;
+ last_transparency = rls->transparency_mask;
+ rlci->occlusion = last_occlusion;
+ rls = rls->next;
+ for (; rls; rls = rls->next) {
+ double gpos[3], lpos[3];
+ double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
+ double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
+ interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
+ POS_TO_FLOAT(lpos, gpos)
+ lineart_chain_append_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ new_rl->flags,
+ rls->occlusion,
+ rls->transparency_mask,
+ new_rl->r_obindex);
+ last_occlusion = rls->occlusion;
+ last_transparency = rls->transparency_mask;
+ }
+ VERT_COORD_TO_FLOAT(new_rl->r)
+ lineart_chain_append_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ new_rl->flags,
+ last_occlusion,
+ last_transparency,
+ new_rl->r_obindex);
+ }
+ ba = ED_lineart_get_point_bounding_area_deep(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ }
+ if (rb->fuzzy_everything) {
+ rlc->type = LRT_EDGE_FLAG_CONTOUR;
+ }
+ else {
+ rlc->type = (rl->flags & LRT_EDGE_FLAG_ALL_TYPE);
+ }
+ }
+ LRT_ITER_ALL_LINES_END
+}
+
+static LineartBoundingArea *lineart_bounding_area_get_rlci_recursive(
+ LineartRenderBuffer *rb, LineartBoundingArea *root, LineartRenderLineChainItem *rlci)
+{
+ if (root->child == NULL) {
+ return root;
+ }
+ else {
+ LineartBoundingArea *ch = root->child;
+#define IN_BOUND(ba, rlci) \
+ ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
+
+ if (IN_BOUND(ch[0], rlci)) {
+ return lineart_bounding_area_get_rlci_recursive(rb, &ch[0], rlci);
+ }
+ else if (IN_BOUND(ch[1], rlci)) {
+ return lineart_bounding_area_get_rlci_recursive(rb, &ch[1], rlci);
+ }
+ else if (IN_BOUND(ch[2], rlci)) {
+ return lineart_bounding_area_get_rlci_recursive(rb, &ch[2], rlci);
+ }
+ else if (IN_BOUND(ch[3], rlci)) {
+ return lineart_bounding_area_get_rlci_recursive(rb, &ch[3], rlci);
+ }
+#undef IN_BOUND
+ }
+ return NULL;
+}
+
+static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuffer *rb,
+ LineartRenderLineChainItem *rlci)
+{
+ if (!rlci) {
+ return NULL;
+ }
+ LineartBoundingArea *root = ED_lineart_get_point_bounding_area(rb, rlci->pos[0], rlci->pos[1]);
+ if (root == NULL) {
+ return NULL;
+ }
+ return lineart_bounding_area_get_rlci_recursive(rb, root, rlci);
+}
+
+/* if reduction threshold is even larger than a small bounding area,. */
+/* then 1) geometry is simply too dense. */
+/* 2) probably need to add it to root bounding area which has larger surface area then it
+ * will. */
+/* cover typical threshold values. */
+static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb,
+ LineartBoundingArea *root,
+ LineartRenderLineChain *rlc,
+ LineartRenderLineChainItem *rlci)
+{
+ if (root->child == NULL) {
+ LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized(
+ &root->linked_chains, &rb->render_data_pool, rlc, sizeof(LineartChainRegisterEntry));
+
+ cre->rlci = rlci;
+
+ if (rlci == rlc->chain.first) {
+ cre->is_left = 1;
+ }
+ }
+ else {
+ LineartBoundingArea *ch = root->child;
+
+#define IN_BOUND(ba, rlci) \
+ ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
+
+ if (IN_BOUND(ch[0], rlci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[0], rlc, rlci);
+ }
+ else if (IN_BOUND(ch[1], rlci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[1], rlc, rlci);
+ }
+ else if (IN_BOUND(ch[2], rlci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[2], rlc, rlci);
+ }
+ else if (IN_BOUND(ch[3], rlci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[3], rlc, rlci);
+ }
+
+#undef IN_BOUND
+ }
+}
+
+static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartRenderLineChain *rlc)
+{
+ LineartRenderLineChainItem *pl = rlc->chain.first;
+ LineartRenderLineChainItem *pr = rlc->chain.last;
+ LineartBoundingArea *ba1 = ED_lineart_get_point_bounding_area(rb, pl->pos[0], pl->pos[1]);
+ LineartBoundingArea *ba2 = ED_lineart_get_point_bounding_area(rb, pr->pos[0], pr->pos[1]);
+
+ if (ba1) {
+ lineart_bounding_area_link_point_recursive(rb, ba1, rlc, pl);
+ }
+ if (ba2) {
+ lineart_bounding_area_link_point_recursive(rb, ba2, rlc, pr);
+ }
+}
+
+void ED_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
+{
+ LineartRenderLineChain *rlc, *new_rlc;
+ LineartRenderLineChainItem *rlci, *next_rlci;
+ ListBase swap = {0};
+
+ swap.first = rb->chains.first;
+ swap.last = rb->chains.last;
+
+ rb->chains.last = rb->chains.first = NULL;
+
+ while ((rlc = BLI_pophead(&swap)) != NULL) {
+ rlc->next = rlc->prev = NULL;
+ BLI_addtail(&rb->chains, rlc);
+ LineartRenderLineChainItem *first_rlci = (LineartRenderLineChainItem *)rlc->chain.first;
+ int fixed_occ = first_rlci->occlusion;
+ unsigned char fixed_mask = first_rlci->transparency_mask;
+ rlc->level = fixed_occ;
+ rlc->transparency_mask = fixed_mask;
+ for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
+ next_rlci = rlci->next;
+ if (rlci->occlusion != fixed_occ || rlci->transparency_mask != fixed_mask) {
+ if (next_rlci) {
+ if (lineart_point_overlapping(next_rlci, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ continue;
+ }
+ }
+ else {
+ /* Set the same occlusion level for the end vertex, so when further connection is needed
+ * the backwards occlusion info is also correct. */
+ rlci->occlusion = fixed_occ;
+ /* No need to split at the last point anyway. */
+ break;
+ }
+ new_rlc = lineart_chain_create(rb);
+ new_rlc->chain.first = rlci;
+ new_rlc->chain.last = rlc->chain.last;
+ rlc->chain.last = rlci->prev;
+ ((LineartRenderLineChainItem *)rlc->chain.last)->next = 0;
+ rlci->prev = 0;
+
+ /* end the previous one. */
+ lineart_chain_append_point(rb,
+ rlc,
+ rlci->pos,
+ rlci->gpos,
+ rlci->normal,
+ rlci->line_type,
+ fixed_occ,
+ fixed_mask,
+ rlci->index);
+ new_rlc->object_ref = rlc->object_ref;
+ new_rlc->type = rlc->type;
+ rlc = new_rlc;
+ fixed_occ = rlci->occlusion;
+ fixed_mask = rlci->transparency_mask;
+ rlc->level = fixed_occ;
+ rlc->transparency_mask = fixed_mask;
+ }
+ }
+ }
+ LISTBASE_FOREACH (LineartRenderLineChain *, irlc, &rb->chains) {
+ lineart_bounding_area_link_chain(rb, irlc);
+ }
+}
+
+/* note: segment type (crease/material/contour...) is ambiguous after this. */
+static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb),
+ LineartRenderLineChain *onto,
+ LineartRenderLineChain *sub,
+ int reverse_1,
+ int reverse_2)
+{
+ LineartRenderLineChainItem *rlci;
+ if (onto->type == LRT_EDGE_FLAG_INTERSECTION) {
+ if (sub->object_ref) {
+ onto->object_ref = sub->object_ref;
+ onto->type = LRT_EDGE_FLAG_CONTOUR;
+ }
+ }
+ else if (sub->type == LRT_EDGE_FLAG_INTERSECTION) {
+ if (onto->type != LRT_EDGE_FLAG_INTERSECTION) {
+ onto->type = LRT_EDGE_FLAG_CONTOUR;
+ }
+ }
+ if (!reverse_1) { /* L--R L-R. */
+ if (reverse_2) { /* L--R R-L. */
+ BLI_listbase_reverse(&sub->chain);
+ }
+ rlci = sub->chain.first;
+ if (lineart_point_overlapping(onto->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ BLI_pophead(&sub->chain);
+ if (sub->chain.first == NULL) {
+ return;
+ }
+ }
+ ((LineartRenderLineChainItem *)onto->chain.last)->next = sub->chain.first;
+ ((LineartRenderLineChainItem *)sub->chain.first)->prev = onto->chain.last;
+ onto->chain.last = sub->chain.last;
+ }
+ else { /* L-R L--R. */
+ if (!reverse_2) { /* R-L L--R. */
+ BLI_listbase_reverse(&sub->chain);
+ }
+ rlci = onto->chain.first;
+ if (lineart_point_overlapping(sub->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ BLI_pophead(&onto->chain);
+ if (onto->chain.first == NULL) {
+ return;
+ }
+ }
+ ((LineartRenderLineChainItem *)sub->chain.last)->next = onto->chain.first;
+ ((LineartRenderLineChainItem *)onto->chain.first)->prev = sub->chain.last;
+ onto->chain.first = sub->chain.first;
+ }
+}
+
+static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuffer *rb,
+ LineartBoundingArea *ba,
+ LineartRenderLineChain *rlc,
+ LineartRenderLineChainItem *rlci,
+ int occlusion,
+ unsigned char transparency_mask,
+ float dist,
+ int do_geometry_space,
+ float *result_new_len,
+ LineartBoundingArea *caller_ba)
+{
+
+ LineartChainRegisterEntry *closest_cre = NULL;
+
+ /* Keep using for loop because cre could be removed from the iteration before getting to the
+ * next one. */
+ LISTBASE_FOREACH_MUTABLE (LineartChainRegisterEntry *, cre, &ba->linked_chains) {
+ if (cre->rlc->object_ref != rlc->object_ref) {
+ if (!rb->fuzzy_everything) {
+ if (rb->fuzzy_intersections) {
+ /* If none of those are intersection lines... */
+ if ((!(cre->rlc->type & LRT_EDGE_FLAG_INTERSECTION)) &&
+ (!(rlc->type & LRT_EDGE_FLAG_INTERSECTION))) {
+ continue; /* We don't want to chain along different objects at the moment. */
+ }
+ }
+ else {
+ continue;
+ }
+ }
+ }
+ if (cre->rlc->picked || cre->picked) {
+ continue;
+ }
+ if (cre->rlc == rlc || (!cre->rlc->chain.first) || (cre->rlc->level != occlusion) ||
+ (cre->rlc->transparency_mask != transparency_mask)) {
+ continue;
+ }
+ if (!rb->fuzzy_everything) {
+ if (cre->rlc->type != rlc->type) {
+ if (rb->fuzzy_intersections) {
+ if (!(cre->rlc->type == LRT_EDGE_FLAG_INTERSECTION ||
+ rlc->type == LRT_EDGE_FLAG_INTERSECTION)) {
+ continue; /* fuzzy intersetions but no intersection line found. */
+ }
+ }
+ else { /* line type different but no fuzzy. */
+ continue;
+ }
+ }
+ }
+
+ float new_len = do_geometry_space ? len_v3v3(cre->rlci->gpos, rlci->gpos) :
+ len_v2v2(cre->rlci->pos, rlci->pos);
+ if (new_len < dist) {
+ closest_cre = cre;
+ dist = new_len;
+ if (result_new_len) {
+ (*result_new_len) = new_len;
+ }
+ }
+ }
+ float adjacent_new_len =
+ dist; /* We want a closer point anyway. So using modified dist is fine. */
+ LineartChainRegisterEntry *adjacent_closest;
+
+#define LRT_TEST_ADJACENT_AREAS(dist_to, list) \
+ if (dist_to < dist && dist_to > 0) { \
+ LISTBASE_FOREACH (LinkData *, ld, list) { \
+ LineartBoundingArea *sba = (LineartBoundingArea *)ld->data; \
+ adjacent_closest = lineart_chain_get_closest_cre(rb, \
+ sba, \
+ rlc, \
+ rlci, \
+ occlusion, \
+ transparency_mask, \
+ dist, \
+ do_geometry_space, \
+ &adjacent_new_len, \
+ ba); \
+ if (adjacent_new_len < dist) { \
+ dist = adjacent_new_len; \
+ closest_cre = adjacent_closest; \
+ } \
+ } \
+ }
+ if (!do_geometry_space && !caller_ba) {
+ LRT_TEST_ADJACENT_AREAS(rlci->pos[0] - ba->l, &ba->lp);
+ LRT_TEST_ADJACENT_AREAS(ba->r - rlci->pos[0], &ba->rp);
+ LRT_TEST_ADJACENT_AREAS(ba->u - rlci->pos[1], &ba->up);
+ LRT_TEST_ADJACENT_AREAS(rlci->pos[1] - ba->b, &ba->bp);
+ }
+ if (result_new_len) {
+ (*result_new_len) = dist;
+ }
+ return closest_cre;
+}
+
+/* this only does head-tail connection. */
+/* overlapping / tiny isolated segment / loop reduction not implemented here yet. */
+void ED_lineart_chain_connect(LineartRenderBuffer *rb, const int do_geometry_space)
+{
+ LineartRenderLineChain *rlc;
+ LineartRenderLineChainItem *rlci_l, *rlci_r;
+ LineartBoundingArea *ba_l, *ba_r;
+ LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre;
+ float dist = do_geometry_space ? rb->chaining_geometry_threshold : rb->chaining_image_threshold;
+ float dist_l, dist_r;
+ int occlusion, reverse_main;
+ unsigned char transparency_mask;
+ ListBase swap = {0};
+
+ if ((!do_geometry_space && rb->chaining_image_threshold < 0.0001) ||
+ (do_geometry_space && rb->chaining_geometry_threshold < 0.0001)) {
+ return;
+ }
+
+ swap.first = rb->chains.first;
+ swap.last = rb->chains.last;
+
+ rb->chains.last = rb->chains.first = NULL;
+
+ while ((rlc = BLI_pophead(&swap)) != NULL) {
+ rlc->next = rlc->prev = NULL;
+ if (rlc->picked) {
+ continue;
+ }
+ BLI_addtail(&rb->chains, rlc);
+
+ occlusion = rlc->level;
+ transparency_mask = rlc->transparency_mask;
+
+ rlci_l = rlc->chain.first;
+ rlci_r = rlc->chain.last;
+ while ((ba_l = lineart_bounding_area_get_end_point(rb, rlci_l)) &&
+ (ba_r = lineart_bounding_area_get_end_point(rb, rlci_r))) {
+ closest_cre_l = lineart_chain_get_closest_cre(rb,
+ ba_l,
+ rlc,
+ rlci_l,
+ occlusion,
+ transparency_mask,
+ dist,
+ do_geometry_space,
+ &dist_l,
+ NULL);
+ closest_cre_r = lineart_chain_get_closest_cre(rb,
+ ba_r,
+ rlc,
+ rlci_r,
+ occlusion,
+ transparency_mask,
+ dist,
+ do_geometry_space,
+ &dist_r,
+ NULL);
+ if (closest_cre_l && closest_cre_r) {
+ if (dist_l < dist_r) {
+ closest_cre = closest_cre_l;
+ reverse_main = 1;
+ }
+ else {
+ closest_cre = closest_cre_r;
+ reverse_main = 0;
+ }
+ }
+ else if (closest_cre_l) {
+ closest_cre = closest_cre_l;
+ reverse_main = 1;
+ }
+ else if (closest_cre_r) {
+ closest_cre = closest_cre_r;
+ BLI_remlink(&ba_r->linked_chains, closest_cre_r);
+ reverse_main = 0;
+ }
+ else {
+ break;
+ }
+ closest_cre->picked = 1;
+ closest_cre->rlc->picked = 1;
+ if (closest_cre->is_left) {
+ lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 0);
+ }
+ else {
+ lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 1);
+ }
+ BLI_remlink(&swap, closest_cre->rlc);
+ rlci_l = rlc->chain.first;
+ rlci_r = rlc->chain.last;
+ }
+ rlc->picked = 1;
+ }
+}
+
+/* length is in image space. */
+float ED_lineart_chain_compute_length(LineartRenderLineChain *rlc)
+{
+ LineartRenderLineChainItem *rlci;
+ float offset_accum = 0;
+ float dist;
+ float last_point[2];
+
+ rlci = rlc->chain.first;
+ copy_v2_v2(last_point, rlci->pos);
+ for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
+ dist = len_v2v2(rlci->pos, last_point);
+ offset_accum += dist;
+ copy_v2_v2(last_point, rlci->pos);
+ }
+ return offset_accum;
+}
+
+void ED_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold)
+{
+ LineartRenderLineChain *rlc, *next_rlc;
+ for (rlc = rb->chains.first; rlc; rlc = next_rlc) {
+ next_rlc = rlc->next;
+ if (ED_lineart_chain_compute_length(rlc) < threshold) {
+ BLI_remlink(&rb->chains, rlc);
+ }
+ }
+}
+
+int ED_lineart_chain_count(const LineartRenderLineChain *rlc)
+{
+ int count = 0;
+ LISTBASE_FOREACH (LineartRenderLineChainItem *, rlci, &rlc->chain) {
+ count++;
+ }
+ return count;
+}
+
+void ED_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb)
+{
+ if (rb == NULL) {
+ return;
+ }
+ LISTBASE_FOREACH (LineartRenderLineChain *, rlc, &rb->chains) {
+ rlc->picked = 0;
+ }
+}
+
+/* This should always be the last stage!, see the end of
+ * ED_lineart_chain_split_for_fixed_occlusion().*/
+void ED_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad)
+{
+ LineartRenderLineChain *rlc, *new_rlc;
+ LineartRenderLineChainItem *rlci, *next_rlci, *prev_rlci;
+ ListBase swap = {0};
+
+ swap.first = rb->chains.first;
+ swap.last = rb->chains.last;
+
+ rb->chains.last = rb->chains.first = NULL;
+
+ while ((rlc = BLI_pophead(&swap)) != NULL) {
+ rlc->next = rlc->prev = NULL;
+ BLI_addtail(&rb->chains, rlc);
+ LineartRenderLineChainItem *first_rlci = (LineartRenderLineChainItem *)rlc->chain.first;
+ for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
+ next_rlci = rlci->next;
+ prev_rlci = rlci->prev;
+ float angle = M_PI;
+ if (next_rlci && prev_rlci) {
+ angle = angle_v2v2v2(prev_rlci->pos, rlci->pos, next_rlci->pos);
+ }
+ else {
+ break; /* No need to split at the last point anyway.*/
+ }
+ if (angle < angle_threshold_rad) {
+ new_rlc = lineart_chain_create(rb);
+ new_rlc->chain.first = rlci;
+ new_rlc->chain.last = rlc->chain.last;
+ rlc->chain.last = rlci->prev;
+ ((LineartRenderLineChainItem *)rlc->chain.last)->next = 0;
+ rlci->prev = 0;
+
+ /* end the previous one. */
+ lineart_chain_append_point(rb,
+ rlc,
+ rlci->pos,
+ rlci->gpos,
+ rlci->normal,
+ rlci->line_type,
+ rlc->level,
+ rlci->transparency_mask,
+ rlci->index);
+ new_rlc->object_ref = rlc->object_ref;
+ new_rlc->type = rlc->type;
+ new_rlc->level = rlc->level;
+ new_rlc->transparency_mask = rlc->transparency_mask;
+ rlc = new_rlc;
+ }
+ }
+ }
+}
diff --git a/source/blender/editors/lineart/lineart_cpu.c b/source/blender/editors/lineart/lineart_cpu.c
new file mode 100644
index 00000000000..1bc831a8665
--- /dev/null
+++ b/source/blender/editors/lineart/lineart_cpu.c
@@ -0,0 +1,4289 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#include "ED_lineart.h"
+
+#include "BLI_alloca.h"
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math_matrix.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_callbacks.h"
+#include "BKE_camera.h"
+#include "BKE_collection.h"
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_customdata.h"
+#include "BKE_deform.h"
+#include "BKE_editmesh.h"
+#include "BKE_global.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_text.h"
+#include "DEG_depsgraph_query.h"
+#include "DNA_camera_types.h"
+#include "DNA_collection_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_lineart_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 "DNA_text_types.h"
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "BLI_math.h"
+#include "BLI_string_utils.h"
+
+#include "bmesh.h"
+#include "bmesh_class.h"
+#include "bmesh_tools.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "lineart_intern.h"
+
+LineartSharedResource lineart_share;
+
+/* static function declarations. */
+
+static LineartBoundingArea *linear_bounding_areat_first_possible(LineartRenderBuffer *rb,
+ LineartRenderLine *rl);
+
+static void lineart_bounding_area_link_line(LineartRenderBuffer *rb,
+ LineartBoundingArea *root_ba,
+ LineartRenderLine *rl);
+
+static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *This,
+ LineartRenderLine *rl,
+ double x,
+ double y,
+ double k,
+ int positive_x,
+ int positive_y,
+ double *next_x,
+ double *next_y);
+
+static int lineart_get_line_bounding_areas(LineartRenderBuffer *rb,
+ LineartRenderLine *rl,
+ int *rowbegin,
+ int *rowend,
+ int *colbegin,
+ int *colend);
+
+static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
+ LineartBoundingArea *root_ba,
+ LineartRenderTriangle *rt,
+ double *LRUB,
+ int recursive,
+ int recursive_level,
+ bool do_intersection);
+
+static int lineart_triangle_line_imagespace_intersection_v2(SpinLock *spl,
+ const LineartRenderTriangle *rt,
+ const LineartRenderLine *rl,
+ const double *override_camera_loc,
+ const char override_cam_is_persp,
+ const double vp[4][4],
+ const double *camera_dir,
+ const float cam_shift_x,
+ const float cam_shift_y,
+ double *from,
+ double *to);
+
+static void lineart_add_line_to_list(LineartRenderBuffer *rb, LineartRenderLine *rl);
+
+/* Geometry. */
+
+int use_smooth_contour_modifier_contour = 0; /* debug purpose. */
+
+static void lineart_render_line_discard_segment(LineartRenderBuffer *rb,
+ LineartRenderLineSegment *rls)
+{
+ BLI_spin_lock(&rb->lock_cuts);
+
+ memset(rls, 0, sizeof(LineartRenderLineSegment));
+ BLI_addtail(&rb->wasted_cuts, rls);
+
+ BLI_spin_unlock(&rb->lock_cuts);
+}
+
+static LineartRenderLineSegment *lineart_render_line_give_segment(LineartRenderBuffer *rb)
+{
+ BLI_spin_lock(&rb->lock_cuts);
+
+ if (rb->wasted_cuts.first) {
+ LineartRenderLineSegment *rls = (LineartRenderLineSegment *)BLI_pophead(&rb->wasted_cuts);
+ BLI_spin_unlock(&rb->lock_cuts);
+ memset(rls, 0, sizeof(LineartRenderLineSegment));
+ return rls;
+ }
+ else {
+ BLI_spin_unlock(&rb->lock_cuts);
+ return (LineartRenderLineSegment *)lineart_mem_aquire_thread(&rb->render_data_pool,
+ sizeof(LineartRenderLineSegment));
+ }
+}
+
+static void lineart_render_line_cut(LineartRenderBuffer *rb,
+ LineartRenderLine *rl,
+ double start,
+ double end,
+ unsigned char transparency_mask)
+{
+ LineartRenderLineSegment *rls, *irls, *next_rls, *prev_rls;
+ LineartRenderLineSegment *start_segment = 0, *end_segment = 0;
+ LineartRenderLineSegment *ns = 0, *ns2 = 0;
+ int untouched = 0;
+
+ if (LRT_DOUBLE_CLOSE_ENOUGH(start, end)) {
+ return;
+ }
+
+ if (LRT_DOUBLE_CLOSE_ENOUGH(start, 1) || LRT_DOUBLE_CLOSE_ENOUGH(end, 0)) {
+ return;
+ }
+
+ if (start != start) {
+ start = 0;
+ }
+ if (end != end) {
+ end = 0;
+ }
+
+ if (start > end) {
+ double t = start;
+ start = end;
+ end = t;
+ }
+
+ /* Keeping the loop in this function for clarity in iterating through the segments. */
+ for (rls = rl->segments.first; rls; rls = rls->next) {
+ if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, start)) {
+ start_segment = rls;
+ ns = start_segment;
+ break;
+ }
+ if (rls->next == NULL) {
+ break;
+ }
+ irls = rls->next;
+ if (irls->at > start + 1e-09 && start > rls->at) {
+ start_segment = irls;
+ ns = lineart_mem_aquire_thread(&rb->render_data_pool, sizeof(LineartRenderLineSegment));
+ break;
+ }
+ }
+ if (!start_segment && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ untouched = 1;
+ }
+ for (rls = start_segment; rls; rls = rls->next) {
+ if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, end)) {
+ end_segment = rls;
+ ns2 = end_segment;
+ break;
+ }
+ /* irls = rls->next;. */
+ /* added this to prevent rls->at == 1.0 (we don't need an end point for this). */
+ if (!rls->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ end_segment = rls;
+ ns2 = end_segment;
+ untouched = 1;
+ break;
+ }
+ else if (rls->at > end) {
+ end_segment = rls;
+ ns2 = lineart_render_line_give_segment(rb);
+ break;
+ }
+ }
+
+ if (ns == NULL) {
+ ns = lineart_render_line_give_segment(rb);
+ }
+ if (ns2 == NULL) {
+ if (untouched) {
+ ns2 = ns;
+ end_segment = ns2;
+ }
+ else
+ ns2 = lineart_render_line_give_segment(rb);
+ }
+
+ if (start_segment) {
+ if (start_segment != ns) {
+ ns->occlusion = start_segment->prev ? (irls = start_segment->prev)->occlusion : 0;
+ BLI_insertlinkbefore(&rl->segments, (void *)start_segment, (void *)ns);
+ }
+ }
+ else {
+ ns->occlusion = (irls = rl->segments.last)->occlusion;
+ BLI_addtail(&rl->segments, ns);
+ }
+ if (end_segment) {
+ if (end_segment != ns2) {
+ ns2->occlusion = end_segment->prev ? (irls = end_segment->prev)->occlusion : 0;
+ BLI_insertlinkbefore(&rl->segments, (void *)end_segment, (void *)ns2);
+ }
+ }
+ else {
+ ns2->occlusion = (irls = rl->segments.last)->occlusion;
+ BLI_addtail(&rl->segments, ns2);
+ }
+
+ ns->at = start;
+ if (!untouched) {
+ ns2->at = end;
+ }
+ else {
+ ns2 = ns2->next;
+ }
+
+ for (rls = ns; rls && rls != ns2; rls = rls->next) {
+ rls->occlusion++;
+ rls->transparency_mask |= transparency_mask;
+ }
+
+ char min_occ = 127;
+ prev_rls = NULL;
+ for (rls = rl->segments.first; rls; rls = next_rls) {
+ next_rls = rls->next;
+
+ if (prev_rls && prev_rls->occlusion == rls->occlusion &&
+ prev_rls->transparency_mask == rls->transparency_mask) {
+ BLI_remlink(&rl->segments, rls);
+ lineart_render_line_discard_segment(rb, rls);
+ continue;
+ }
+
+ min_occ = MIN2(min_occ, rls->occlusion);
+
+ prev_rls = rls;
+ }
+ rl->min_occ = min_occ;
+}
+
+static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRenderTaskInfo *rti)
+{
+ LineartRenderLine *data;
+ int i;
+ int res = 0;
+
+ BLI_spin_lock(&rb->lock_task);
+
+#define LRT_ASSIGN_OCCLUSION_TASK(name) \
+ if (rb->name##_managed) { \
+ data = rb->name##_managed; \
+ rti->name = (void *)data; \
+ for (i = 0; i < LRT_THREAD_LINE_COUNT && data; i++) { \
+ data = data->next; \
+ } \
+ rti->name##_end = data; \
+ rb->name##_managed = data; \
+ res = 1; \
+ } \
+ else { \
+ rti->name = NULL; \
+ }
+
+ LRT_ASSIGN_OCCLUSION_TASK(contour);
+ LRT_ASSIGN_OCCLUSION_TASK(intersection);
+ LRT_ASSIGN_OCCLUSION_TASK(crease);
+ LRT_ASSIGN_OCCLUSION_TASK(material);
+ LRT_ASSIGN_OCCLUSION_TASK(edge_mark);
+
+#undef LRT_ASSIGN_OCCLUSION_TASK
+
+ BLI_spin_unlock(&rb->lock_task);
+
+ return res;
+}
+
+BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartRenderLine *rl,
+ LineartRenderTriangle *rt)
+{
+ LineartRenderVertIntersection *l = (void *)rl->l;
+ LineartRenderVertIntersection *r = (void *)rl->r;
+ return ((l->base.flag && l->intersecting_with == (void *)rt) ||
+ (r->base.flag && r->intersecting_with == (void *)rt));
+}
+
+static void lineart_occlusion_single_line(LineartRenderBuffer *rb,
+ LineartRenderLine *rl,
+ int thread_id)
+{
+ double x = rl->l->fbcoord[0], y = rl->l->fbcoord[1];
+ LineartBoundingArea *ba = linear_bounding_areat_first_possible(rb, rl);
+ LineartBoundingArea *nba = ba;
+ LineartRenderTriangleThread *rt;
+
+ double l, r;
+ double k = (rl->r->fbcoord[1] - rl->l->fbcoord[1]) /
+ (rl->r->fbcoord[0] - rl->l->fbcoord[0] + 1e-30);
+ int positive_x = (rl->r->fbcoord[0] - rl->l->fbcoord[0]) > 0 ?
+ 1 :
+ (rl->r->fbcoord[0] == rl->l->fbcoord[0] ? 0 : -1);
+ int positive_y = (rl->r->fbcoord[1] - rl->l->fbcoord[1]) > 0 ?
+ 1 :
+ (rl->r->fbcoord[1] == rl->l->fbcoord[1] ? 0 : -1);
+
+ while (nba) {
+
+ LISTBASE_FOREACH (LinkData *, lip, &nba->linked_triangles) {
+ rt = lip->data;
+ if (rt->testing[thread_id] == rl || (rt->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) ||
+ lineart_occlusion_is_adjacent_intersection(rl, (LineartRenderTriangle *)rt)) {
+ continue;
+ }
+ rt->testing[thread_id] = rl;
+ if (lineart_triangle_line_imagespace_intersection_v2(&rb->lock_task,
+ (void *)rt,
+ rl,
+ rb->camera_pos,
+ rb->cam_is_persp,
+ rb->view_projection,
+ rb->view_vector,
+ rb->shift_x,
+ rb->shift_y,
+ &l,
+ &r)) {
+ lineart_render_line_cut(rb, rl, l, r, rt->base.transparency_mask);
+ if (rl->min_occ > rb->max_occlusion_level) {
+ return; /* No need to caluclate any longer. */
+ }
+ }
+ }
+ nba = lineart_bounding_area_next(nba, rl, x, y, k, positive_x, positive_y, &x, &y);
+ }
+}
+
+static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartRenderTaskInfo *rti)
+{
+ LineartRenderBuffer *rb = lineart_share.render_buffer_shared;
+ LineartRenderLine *lip;
+
+ while (lineart_occlusion_make_task_info(rb, rti)) {
+
+ for (lip = (void *)rti->contour; lip && lip != rti->contour_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+
+ /* Monitoring cancelation flag every once a while. */
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ return;
+ }
+
+ for (lip = (void *)rti->crease; lip && lip != rti->crease_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ return;
+ }
+
+ for (lip = (void *)rti->intersection; lip && lip != rti->intersection_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ return;
+ }
+
+ for (lip = (void *)rti->material; lip && lip != rti->material_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ return;
+ }
+
+ for (lip = (void *)rti->edge_mark; lip && lip != rti->edge_mark_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ return;
+ }
+ }
+}
+
+static void lineart_main_occlusion_begin(LineartRenderBuffer *rb)
+{
+ int thread_count = lineart_share.thread_count;
+ LineartRenderTaskInfo *rti = MEM_callocN(sizeof(LineartRenderTaskInfo) * thread_count,
+ "Task Pool");
+ int i;
+
+ rb->contour_managed = rb->contours;
+ rb->crease_managed = rb->crease_lines;
+ rb->intersection_managed = rb->intersection_lines;
+ rb->material_managed = rb->material_lines;
+ rb->edge_mark_managed = rb->edge_marks;
+
+ TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH);
+
+ for (i = 0; i < thread_count; i++) {
+ rti[i].thread_id = i;
+ BLI_task_pool_push(tp, (TaskRunFunction)lineart_occlusion_worker, &rti[i], 0, NULL);
+ }
+ BLI_task_pool_work_and_wait(tp);
+ BLI_task_pool_free(tp);
+
+ MEM_freeN(rti);
+}
+
+int ED_lineart_point_inside_triangled(double v[2], double v0[2], double v1[2], double v2[2])
+{
+ double cl, c;
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ c = cl;
+
+ cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]);
+ if (c * cl <= 0) {
+ return 0;
+ }
+ else
+ c = cl;
+
+ cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]);
+ if (c * cl <= 0) {
+ return 0;
+ }
+ else
+ c = cl;
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ if (c * cl <= 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int lineart_point_on_segment(double v[2], double v0[2], double v1[2])
+{
+ double c1 = 0, c2 = 0;
+ double l0[2], l1[2];
+
+ sub_v2_v2v2_db(l0, v, v0);
+ sub_v2_v2v2_db(l1, v, v1);
+
+ if (v1[0] == v0[0] && v1[1] == v0[1]) {
+ return 0;
+ }
+
+ if (v1[0] - v0[0]) {
+ c1 = ratiod(v0[0], v1[0], v[0]);
+ }
+ else {
+ if (v[0] == v1[0]) {
+ c2 = ratiod(v0[1], v1[1], v[1]);
+ return (c2 >= 0 && c2 <= 1);
+ }
+ }
+
+ if (v1[1] - v0[1]) {
+ c2 = ratiod(v0[1], v1[1], v[1]);
+ }
+ else {
+ if (v[1] == v1[1]) {
+ c1 = ratiod(v0[0], v1[0], v[0]);
+ return (c1 >= 0 && c1 <= 1);
+ }
+ }
+
+ if (LRT_DOUBLE_CLOSE_ENOUGH(c1, c2) && c1 >= 0 && c1 <= 1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int lineart_point_triangle_relation(double v[2], double v0[2], double v1[2], double v2[2])
+{
+ double cl, c;
+ double r;
+ if (lineart_point_on_segment(v, v0, v1) || lineart_point_on_segment(v, v1, v2) ||
+ lineart_point_on_segment(v, v2, v0)) {
+ return 1;
+ }
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ c = cl;
+
+ cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]);
+ if ((r = c * cl) < 0) {
+ return 0;
+ }
+ else
+ c = cl;
+
+ cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]);
+ if ((r = c * cl) < 0) {
+ return 0;
+ }
+ else
+ c = cl;
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ if ((r = c * cl) < 0) {
+ return 0;
+ }
+ else if (r == 0) {
+ return 1;
+ }
+
+ return 2;
+}
+
+static int lineart_point_inside_triangle3de(double v[3], double v0[3], double v1[3], double v2[3])
+{
+ double l[3], r[3];
+ double N1[3], N2[3];
+ double d;
+
+ sub_v3_v3v3_db(l, v1, v0);
+ sub_v3_v3v3_db(r, v, v1);
+ cross_v3_v3v3_db(N1, l, r);
+
+ sub_v3_v3v3_db(l, v2, v1);
+ sub_v3_v3v3_db(r, v, v2);
+ cross_v3_v3v3_db(N2, l, r);
+
+ if ((d = dot_v3v3_db(N1, N2)) < 0) {
+ return 0;
+ }
+
+ sub_v3_v3v3_db(l, v0, v2);
+ sub_v3_v3v3_db(r, v, v0);
+ cross_v3_v3v3_db(N1, l, r);
+
+ if ((d = dot_v3v3_db(N1, N2)) < 0) {
+ return 0;
+ }
+
+ sub_v3_v3v3_db(l, v1, v0);
+ sub_v3_v3v3_db(r, v, v1);
+ cross_v3_v3v3_db(N2, l, r);
+
+ if ((d = dot_v3v3_db(N1, N2)) < 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static LineartRenderElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb)
+{
+ LineartRenderElementLinkNode *reln;
+
+ LineartRenderTriangle *render_triangles = lineart_mem_aquire(
+ &rb->render_data_pool,
+ 64 * rb->triangle_size); /* CreateNewBuffer(LineartRenderTriangle, 64);. */
+
+ reln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers,
+ &rb->render_data_pool,
+ render_triangles,
+ sizeof(LineartRenderElementLinkNode));
+ reln->element_count = 64;
+ reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+
+ return reln;
+}
+
+static LineartRenderElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer *rb)
+{
+ LineartRenderElementLinkNode *reln;
+
+ LineartRenderVert *render_vertices = lineart_mem_aquire(&rb->render_data_pool,
+ sizeof(LineartRenderVert) * 64);
+
+ reln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers,
+ &rb->render_data_pool,
+ render_vertices,
+ sizeof(LineartRenderElementLinkNode));
+ reln->element_count = 64;
+ reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+
+ return reln;
+}
+
+static LineartRenderElementLinkNode *lineart_memory_get_line_space(LineartRenderBuffer *rb)
+{
+ LineartRenderElementLinkNode *reln;
+
+ LineartRenderLine *render_lines = lineart_mem_aquire(&rb->render_data_pool,
+ sizeof(LineartRenderLine) * 64);
+
+ reln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers,
+ &rb->render_data_pool,
+ render_lines,
+ sizeof(LineartRenderElementLinkNode));
+ reln->element_count = 64;
+ reln->crease_threshold = rb->crease_threshold;
+ reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+
+ return reln;
+}
+
+static void lineart_triangle_post(LineartRenderTriangle *rt, LineartRenderTriangle *orig)
+{
+ copy_v3_v3_db(rt->gn, orig->gn);
+ rt->flags = LRT_CULL_GENERATED;
+}
+
+static void lineart_triangle_set_cull_flag(LineartRenderTriangle *rt, unsigned char flag)
+{
+ unsigned char intersection_only = (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY);
+ rt->flags = flag;
+ rt->flags |= intersection_only;
+}
+
+static bool lineart_line_match(LineartRenderTriangle *rt, LineartRenderLine *rl, int v1, int v2)
+{
+ return ((rt->v[v1] == rl->l && rt->v[v2] == rl->r) ||
+ (rt->v[v2] == rl->l && rt->v[v1] == rl->r));
+}
+
+static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
+ LineartRenderTriangle *rt,
+ int in0,
+ int in1,
+ int in2,
+ double *cam_pos,
+ double *view_dir,
+ bool allow_boundaries,
+ double (*vp)[4],
+ Object *ob,
+ int *r_v_count,
+ int *r_l_count,
+ int *r_t_count,
+ LineartRenderElementLinkNode *veln,
+ LineartRenderElementLinkNode *leln,
+ LineartRenderElementLinkNode *teln)
+{
+ double vv1[3], vv2[3], dot1, dot2;
+ double a;
+ int v_count = *r_v_count;
+ int l_count = *r_l_count;
+ int t_count = *r_t_count;
+ int l_obi, r_obi;
+ char new_flag = 0;
+
+ LineartRenderLine *new_rl, *rl, *old_rl;
+ LineartRenderLineSegment *rls;
+ LineartRenderTriangleAdjacent *rta;
+
+ if (rt->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) {
+ return;
+ }
+
+ /* See header and object_load() for details. */
+ rta = (void *)rt->intersecting_verts;
+
+ LineartRenderVert *rv = &((LineartRenderVert *)veln->pointer)[v_count];
+ LineartRenderTriangle *rt1 = (void *)(((unsigned char *)teln->pointer) +
+ rb->triangle_size * t_count);
+ LineartRenderTriangle *rt2 = (void *)(((unsigned char *)teln->pointer) +
+ rb->triangle_size * (t_count + 1));
+
+ new_rl = &((LineartRenderLine *)leln->pointer)[l_count];
+ rl = new_rl;
+
+#define INCREASE_RL \
+ l_count++; \
+ l_obi = rl->l_obindex; \
+ r_obi = rl->r_obindex; \
+ new_rl = &((LineartRenderLine *)leln->pointer)[l_count]; \
+ rl = new_rl; \
+ rl->l_obindex = l_obi; \
+ rl->r_obindex = r_obi; \
+ rls = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLineSegment)); \
+ BLI_addtail(&rl->segments, rls);
+
+#define SELECT_RL(rl_num, llink, rlink, newrt) \
+ if (rta->rl[rl_num]) { \
+ old_rl = rta->rl[rl_num]; \
+ new_flag = old_rl->flags; \
+ old_rl->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ INCREASE_RL \
+ rl->l = (llink); \
+ rl->r = (rlink); \
+ rl->flags = new_flag; \
+ rl->object_ref = ob; \
+ rl->tl = ((old_rl->tl == rt) ? (newrt) : (old_rl->tl)); \
+ rl->tr = ((old_rl->tr == rt) ? (newrt) : (old_rl->tr)); \
+ lineart_add_line_to_list(rb, rl); \
+ }
+
+#define RELINK_RL(rl_num, newrt) \
+ if (rta->rl[rl_num]) { \
+ old_rl = rta->rl[rl_num]; \
+ old_rl->tl = ((old_rl->tl == rt) ? (newrt) : (old_rl->tl)); \
+ old_rl->tr = ((old_rl->tr == rt) ? (newrt) : (old_rl->tr)); \
+ }
+
+#define REMOVE_TRIANGLE_RL \
+ if (rta->rl[0]) { \
+ rta->rl[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ } \
+ if (rta->rl[1]) { \
+ rta->rl[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ } \
+ if (rta->rl[2]) { \
+ rta->rl[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ }
+
+ switch (in0 + in1 + in2) {
+ case 0: /* ignore this triangle. */
+ return;
+ case 3:
+ /** triangle completely behind near plane, throw it away
+ * also remove render lines form being computed.
+ . */
+ lineart_triangle_set_cull_flag(rt, LRT_CULL_DISCARD);
+ REMOVE_TRIANGLE_RL
+ return;
+ case 2:
+ /** Two points behind near plane, cut those and
+ * generate 2 new points, 3 lines and 1 triangle. */
+ lineart_triangle_set_cull_flag(rt, LRT_CULL_USED);
+
+ /** (!in0) means "when point 0 is visible".
+ * conditons for point 1, 2 are the same idea.
+ * 1-----|-------0
+ * | | ---
+ * | |---
+ * | ---|
+ * 2-- |
+ * (near)---------->(far)
+ * Will become:
+ * |N******0
+ * |* ***
+ * |N**
+ * |
+ * |
+ * (near)---------->(far)
+ . */
+ if (!in0) {
+
+ /* cut point for line 2---|-----0. */
+ sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ /* assign it to a new point. */
+ interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a);
+ mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
+ rv[0].index = rt->v[2]->index;
+
+ /* cut point for line 1---|-----0. */
+ sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ /* assign it to another new point. */
+ interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a);
+ mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
+ rv[1].index = rt->v[1]->index;
+
+ /* New line connecting two new points. */
+ INCREASE_RL
+ if (allow_boundaries) {
+ rl->flags = LRT_EDGE_FLAG_CONTOUR;
+ lineart_prepend_line_direct(&rb->contours, rl);
+ }
+ /** note: inverting rl->l/r (left/right point) doesn't matter as long as
+ * rt->rl and rt->v has the same sequence. and the winding direction
+ * can be either CW or CCW but needs to be consistent throughout the calculation.
+ . */
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ /* only one adjacent triangle, because the other side is the near plane. */
+ /* use tl or tr doesn't matter. */
+ rl->tl = rt1;
+ rl->object_ref = ob;
+
+ /* new line connecting original point 0 and a new point, only when it's a selected line. */
+ SELECT_RL(2, rt->v[0], &rv[0], rt1)
+ /* new line connecting original point 0 and another new point. */
+ SELECT_RL(0, rt->v[0], &rv[1], rt1)
+
+ /* re-assign triangle point array to two new points. */
+ rt1->v[0] = rt->v[0];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = &rv[0];
+
+ lineart_triangle_post(rt1, rt);
+
+ v_count += 2;
+ t_count += 1;
+ }
+ else if (!in2) {
+ sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a);
+ mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
+ rv[0].index = rt->v[0]->index;
+
+ sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a);
+ mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
+ rv[1].index = rt->v[1]->index;
+
+ INCREASE_RL
+ if (allow_boundaries) {
+ rl->flags = LRT_EDGE_FLAG_CONTOUR;
+ lineart_prepend_line_direct(&rb->contours, rl);
+ }
+ rl->l = &rv[0];
+ rl->r = &rv[1];
+ rl->tl = rt1;
+ rl->object_ref = ob;
+
+ SELECT_RL(2, rt->v[2], &rv[0], rt1)
+ SELECT_RL(1, rt->v[2], &rv[1], rt1)
+
+ rt1->v[0] = &rv[0]; /*&rv[1];*/
+ rt1->v[1] = &rv[1]; /*rt->v[2];*/
+ rt1->v[2] = rt->v[2]; /*&rv[0];*/
+
+ lineart_triangle_post(rt1, rt);
+
+ v_count += 2;
+ t_count += 1;
+ }
+ else if (!in1) {
+ sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a);
+ mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
+ rv[0].index = rt->v[2]->index;
+
+ sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a);
+ mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
+ rv[1].index = rt->v[0]->index;
+
+ INCREASE_RL
+ if (allow_boundaries) {
+ rl->flags = LRT_EDGE_FLAG_CONTOUR;
+ lineart_prepend_line_direct(&rb->contours, rl);
+ }
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rl->object_ref = ob;
+
+ SELECT_RL(1, rt->v[1], &rv[0], rt1)
+ SELECT_RL(0, rt->v[1], &rv[1], rt1)
+
+ rt1->v[0] = &rv[0]; /*rt->v[1];*/
+ rt1->v[1] = rt->v[1]; /*&rv[1];*/
+ rt1->v[2] = &rv[1]; /*&rv[0];*/
+
+ lineart_triangle_post(rt1, rt);
+
+ v_count += 2;
+ t_count += 1;
+ }
+ break;
+ case 1:
+ /** Two points behind near plane, cut those and
+ * generate 2 new points, 4 lines and 2 triangles. */
+ lineart_triangle_set_cull_flag(rt, LRT_CULL_USED);
+
+ /** (in0) means "when point 0 is invisible".
+ * conditons for point 1, 2 are the same idea.
+ * 0------|----------1
+ * -- | |
+ * ---| |
+ * |-- |
+ * | --- |
+ * | --- |
+ * | --2
+ * (near)---------->(far)
+ * Will become:
+ * |N*********1
+ * |* *** |
+ * |* *** |
+ * |N** |
+ * | *** |
+ * | *** |
+ * | **2
+ * (near)---------->(far)
+ . */
+ if (in0) {
+ /* Cut point for line 0---|------1. */
+ sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot2 / (dot1 + dot2);
+ /* Assign to a new point. */
+ interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a);
+ mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
+ rv[0].index = rt->v[0]->index;
+
+ /* Cut point for line 0---|------2. */
+ sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot2 / (dot1 + dot2);
+ /* Assign to aother new point. */
+ interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a);
+ mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
+ rv[1].index = rt->v[0]->index;
+
+ /* New line connects two new points. */
+ INCREASE_RL
+ if (allow_boundaries) {
+ rl->flags = LRT_EDGE_FLAG_CONTOUR;
+ lineart_prepend_line_direct(&rb->contours, rl);
+ }
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rl->object_ref = ob;
+
+ /** New line connects new point 0 and old point 1,
+ * this is a border line.
+ . */
+
+ SELECT_RL(0, rt->v[1], &rv[0], rt1)
+ SELECT_RL(2, rt->v[2], &rv[1], rt2)
+ RELINK_RL(1, rt2)
+
+ /* We now have one triangle closed. */
+ rt1->v[0] = rt->v[1];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = &rv[0];
+ /* Close the second triangle. */
+ rt2->v[0] = &rv[1];
+ rt2->v[1] = rt->v[1];
+ rt2->v[2] = rt->v[2];
+
+ lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(rt2, rt);
+
+ v_count += 2;
+ t_count += 2;
+ }
+ else if (in1) {
+
+ sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a);
+ mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
+ rv[0].index = rt->v[1]->index;
+
+ sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a);
+ mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
+ rv[1].index = rt->v[1]->index;
+
+ INCREASE_RL
+ if (allow_boundaries) {
+ rl->flags = LRT_EDGE_FLAG_CONTOUR;
+ lineart_prepend_line_direct(&rb->contours, rl);
+ }
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rl->object_ref = ob;
+
+ SELECT_RL(1, rt->v[2], &rv[0], rt1)
+ SELECT_RL(0, rt->v[0], &rv[1], rt2)
+ RELINK_RL(2, rt2)
+
+ rt1->v[0] = rt->v[2];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = &rv[0];
+
+ rt2->v[0] = &rv[1];
+ rt2->v[1] = rt->v[2];
+ rt2->v[2] = rt->v[0];
+
+ lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(rt2, rt);
+
+ v_count += 2;
+ t_count += 2;
+ }
+ else if (in2) {
+
+ sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a);
+ mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
+ rv[0].index = rt->v[2]->index;
+
+ sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ dot1 = dot_v3v3_db(vv1, view_dir);
+ dot2 = dot_v3v3_db(vv2, view_dir);
+ a = dot1 / (dot1 + dot2);
+ interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a);
+ mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
+ rv[1].index = rt->v[2]->index;
+
+ INCREASE_RL
+ if (allow_boundaries) {
+ rl->flags = LRT_EDGE_FLAG_CONTOUR;
+ lineart_prepend_line_direct(&rb->contours, rl);
+ }
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rl->object_ref = ob;
+
+ SELECT_RL(2, rt->v[0], &rv[0], rt1)
+ SELECT_RL(1, rt->v[1], &rv[1], rt2)
+ RELINK_RL(0, rt2)
+
+ rt1->v[0] = rt->v[0];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = &rv[0];
+
+ rt2->v[0] = &rv[1];
+ rt2->v[1] = rt->v[0];
+ rt2->v[2] = rt->v[1];
+
+ lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(rt2, rt);
+
+ v_count += 2;
+ t_count += 2;
+ }
+ break;
+ }
+ *r_v_count = v_count;
+ *r_l_count = l_count;
+ *r_t_count = t_count;
+
+#undef INCREASE_RL
+#undef SELECT_RL
+#undef RELINK_RL
+#undef REMOVE_TRIANGLE_RL
+}
+
+/** This function cuts triangles that are (partially or fully) behind near clipping plane.
+ * for triangles that crossing the near plane, it will generate new 1 or 2 triangles with
+ * new topology that represents the trimmed triangle. (which then became a triangle or square)
+ */
+static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
+{
+ LineartRenderTriangle *rt;
+ LineartRenderElementLinkNode *veln, *teln, *leln;
+ double(*vp)[4] = rb->view_projection;
+ int i;
+ int v_count = 0, t_count = 0, l_count = 0;
+ Object *ob;
+ bool allow_boundaries = rb->allow_boundaries;
+ double cam_pos[3];
+ double clip_start = rb->near_clip, clip_end = rb->far_clip;
+ double view_dir[3], clip_advance[3];
+
+ copy_v3_v3_db(view_dir, rb->view_vector);
+ copy_v3_v3_db(clip_advance, rb->view_vector);
+ copy_v3_v3_db(cam_pos, rb->camera_pos);
+
+ if (clip_far) {
+ /* Move starting point to end plane. */
+ mul_v3db_db(clip_advance, -clip_end);
+ add_v3_v3_db(cam_pos, clip_advance);
+
+ /* "reverse looking". */
+ mul_v3db_db(view_dir, -1.0f);
+ }
+ else { /* Clip Near. */
+ mul_v3db_db(clip_advance, -clip_start);
+ add_v3_v3_db(cam_pos, clip_advance);
+ }
+
+ veln = lineart_memory_get_vert_space(rb);
+ teln = lineart_memory_get_triangle_space(rb);
+ leln = lineart_memory_get_line_space(rb);
+
+ /* Additional memory space for storing generated points and triangles. */
+#define LRT_CULL_ENSURE_MEMORY \
+ if (v_count > 60) { \
+ veln->element_count = v_count; \
+ veln = lineart_memory_get_vert_space(rb); \
+ v_count = 0; \
+ } \
+ if (t_count > 60) { \
+ teln->element_count = t_count; \
+ teln = lineart_memory_get_triangle_space(rb); \
+ t_count = 0; \
+ } \
+ if (l_count > 60) { \
+ leln->element_count = l_count; \
+ leln = lineart_memory_get_line_space(rb); \
+ l_count = 0; \
+ }
+
+#define LRT_CULL_DECIDE_INSIDE \
+ /* These three represents points that are in the clipping range or not*/ \
+ in0 = 0, in1 = 0, in2 = 0; \
+ if (clip_far) { \
+ /* Point outside far plane. */ \
+ if (rt->v[0]->fbcoord[use_w] > clip_end) { \
+ in0 = 1; \
+ } \
+ if (rt->v[1]->fbcoord[use_w] > clip_end) { \
+ in1 = 1; \
+ } \
+ if (rt->v[2]->fbcoord[use_w] > clip_end) { \
+ in2 = 1; \
+ } \
+ } \
+ else { \
+ /* Point inside near plane. */ \
+ if (rt->v[0]->fbcoord[use_w] < clip_start) { \
+ in0 = 1; \
+ } \
+ if (rt->v[1]->fbcoord[use_w] < clip_start) { \
+ in1 = 1; \
+ } \
+ if (rt->v[2]->fbcoord[use_w] < clip_start) { \
+ in2 = 1; \
+ } \
+ }
+
+ int use_w = 3;
+ int in0 = 0, in1 = 0, in2 = 0;
+
+ if (!rb->cam_is_persp) {
+ clip_start = -1;
+ clip_end = 1;
+ use_w = 2;
+ }
+
+ /* Then go through all the other triangles. */
+ LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
+ if (reln->flags & LRT_ELEMENT_IS_ADDITIONAL) {
+ continue;
+ }
+ ob = reln->object_ref;
+ for (i = 0; i < reln->element_count; i++) {
+ /* Select the triangle in the array. */
+ rt = (void *)(((unsigned char *)reln->pointer) + rb->triangle_size * i);
+
+ LRT_CULL_DECIDE_INSIDE
+ LRT_CULL_ENSURE_MEMORY
+ lineart_triangle_cull_single(rb,
+ rt,
+ in0,
+ in1,
+ in2,
+ cam_pos,
+ view_dir,
+ allow_boundaries,
+ vp,
+ ob,
+ &v_count,
+ &l_count,
+ &t_count,
+ veln,
+ leln,
+ teln);
+ }
+ teln->element_count = t_count;
+ veln->element_count = v_count;
+ }
+
+#undef LRT_CULL_ENSURE_MEMORY
+#undef LRT_CULL_DECIDE_INSIDE
+}
+
+static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb)
+{
+ LinkData *ld;
+ while ((ld = BLI_pophead(&rb->triangle_adjacent_pointers)) != NULL) {
+ MEM_freeN(ld->data);
+ }
+ LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
+ LineartRenderTriangle *rt = reln->pointer;
+ int i;
+ for (i = 0; i < reln->element_count; i++) {
+ rt->intersecting_verts = NULL;
+ rt = (LineartRenderTriangle *)(((unsigned char *)rt) + rb->triangle_size);
+ }
+ }
+}
+
+static void lineart_main_perspective_division(LineartRenderBuffer *rb)
+{
+ LineartRenderVert *rv;
+ int i;
+ /* float far = rb->far_clip, near = rb->near_clip;*/
+
+ if (!rb->cam_is_persp) {
+ return;
+ }
+
+ LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->vertex_buffer_pointers) {
+ rv = reln->pointer;
+ for (i = 0; i < reln->element_count; i++) {
+ /* Do not divide Z, we use Z to back transform cut points in later chaining process. */
+ rv[i].fbcoord[0] /= rv[i].fbcoord[3];
+ rv[i].fbcoord[1] /= rv[i].fbcoord[3];
+ /* Re-map z into (0-1) range, because we no longer need NDC at the moment. */
+ /* But we don't need actual Z either, we use W for linear depth for back-transform. */
+ /* rv[i].fbcoord[2] = -2 * rv[i].fbcoord[2] / (far - near) - (far + near) / (far - near);. */
+ rv[i].fbcoord[0] -= rb->shift_x * 2;
+ rv[i].fbcoord[1] -= rb->shift_y * 2;
+ }
+ }
+}
+
+static void lineart_vert_transform(
+ BMVert *v, int index, LineartRenderVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4])
+{
+ double co[4];
+ LineartRenderVert *rv = &RvBuf[index];
+ copy_v3db_v3fl(co, v->co);
+ mul_v3_m4v3_db(rv->gloc, mv_mat, co);
+ mul_v4_m4v3_db(rv->fbcoord, mvp_mat, co);
+}
+
+static LineartRenderTriangle *lineart_triangle_from_index(LineartRenderBuffer *rb,
+ LineartRenderTriangle *rt_array,
+ int index)
+{
+ char *b = (char *)rt_array;
+ b += (index * rb->triangle_size);
+ return (LineartRenderTriangle *)b;
+}
+
+static char lineart_test_feature_line(LineartRenderBuffer *rb,
+ BMEdge *e,
+ LineartRenderTriangle *rt_array,
+ LineartRenderVert *rv_array,
+ float crease_threshold,
+ bool no_crease,
+ bool count_freestyle,
+ BMesh *bm_if_freestyle)
+{
+ BMLoop *ll, *lr = NULL;
+ ll = e->l;
+ if (ll) {
+ lr = e->l->radial_next;
+ }
+
+ if (ll == lr || !lr) {
+ return LRT_EDGE_FLAG_CONTOUR;
+ }
+
+ LineartRenderTriangle *rt1, *rt2;
+ LineartRenderVert *l;
+
+ /* We can do so because mesh is already triangulated. */
+ rt1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f));
+ rt2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f));
+
+ l = &rv_array[BM_elem_index_get(e->v1)];
+
+ double vv[3];
+ double *view_vector = vv;
+ double dot_1 = 0, dot_2 = 0;
+ double result;
+ FreestyleEdge *fe;
+
+ if (rb->cam_is_persp) {
+ sub_v3_v3v3_db(view_vector, l->gloc, rb->camera_pos);
+ }
+ else {
+ view_vector = rb->view_vector;
+ }
+
+ dot_1 = dot_v3v3_db(view_vector, rt1->gn);
+ dot_2 = dot_v3v3_db(view_vector, rt2->gn);
+
+ if ((result = dot_1 * dot_2) < 0 && (dot_1 + dot_2)) {
+ return LRT_EDGE_FLAG_CONTOUR;
+ }
+ else if (rb->use_crease && (dot_v3v3_db(rt1->gn, rt2->gn) < crease_threshold)) {
+ if (!no_crease) {
+ return LRT_EDGE_FLAG_CREASE;
+ }
+ }
+ else if (rb->use_material && (ll->f->mat_nr != lr->f->mat_nr)) {
+ return LRT_EDGE_FLAG_MATERIAL;
+ }
+ else if (count_freestyle && rb->use_edge_marks) {
+ fe = CustomData_bmesh_get(&bm_if_freestyle->edata, e->head.data, CD_FREESTYLE_EDGE);
+ if (fe->flag & FREESTYLE_EDGE_MARK) {
+ return LRT_EDGE_FLAG_EDGE_MARK;
+ }
+ }
+ return 0;
+}
+
+static void lineart_add_line_to_list(LineartRenderBuffer *rb, LineartRenderLine *rl)
+{
+ switch (rl->flags) {
+ case LRT_EDGE_FLAG_CONTOUR:
+ lineart_prepend_line_direct(&rb->contours, rl);
+ break;
+ case LRT_EDGE_FLAG_CREASE:
+ lineart_prepend_line_direct(&rb->crease_lines, rl);
+ break;
+ case LRT_EDGE_FLAG_MATERIAL:
+ lineart_prepend_line_direct(&rb->material_lines, rl);
+ break;
+ case LRT_EDGE_FLAG_EDGE_MARK:
+ lineart_prepend_line_direct(&rb->edge_marks, rl);
+ break;
+ case LRT_EDGE_FLAG_INTERSECTION:
+ lineart_prepend_line_direct(&rb->intersection_lines, rl);
+ break;
+ }
+}
+
+static void lineart_triangle_adjacent_assign(LineartRenderTriangle *rt,
+ LineartRenderTriangleAdjacent *rta,
+ LineartRenderLine *rl)
+{
+ if (lineart_line_match(rt, rl, 0, 1)) {
+ rta->rl[0] = rl;
+ }
+ else if (lineart_line_match(rt, rl, 1, 2)) {
+ rta->rl[1] = rl;
+ }
+ else if (lineart_line_match(rt, rl, 2, 0)) {
+ rta->rl[2] = rl;
+ }
+}
+
+static void lineart_geometry_object_load(Depsgraph *dg,
+ Object *ob,
+ double (*mv_mat)[4],
+ double (*mvp_mat)[4],
+ LineartRenderBuffer *rb,
+ int override_usage,
+ int *global_vindex)
+{
+ BMesh *bm;
+ BMVert *v;
+ BMFace *f;
+ BMEdge *e;
+ BMLoop *loop;
+ LineartRenderLine *rl;
+ LineartRenderTriangle *rt;
+ LineartRenderTriangleAdjacent *orta;
+ double new_mvp[4][4], new_mv[4][4], normal[4][4];
+ float imat[4][4];
+ LineartRenderElementLinkNode *reln;
+ LineartRenderVert *orv;
+ LineartRenderLine *orl;
+ LineartRenderTriangle *ort;
+ Object *orig_ob;
+ int CanFindFreestyle = 0;
+ int i, global_i = (*global_vindex);
+ Mesh *use_mesh;
+ float use_crease = 0;
+
+ int usage = override_usage ? override_usage : ob->lineart.usage;
+
+#define LRT_MESH_FINISH \
+ BM_mesh_free(bm); \
+ if (ob->type != OB_MESH) { \
+ BKE_mesh_free(use_mesh); \
+ MEM_freeN(use_mesh); \
+ }
+
+ if (usage == OBJECT_LRT_EXCLUDE) {
+ return;
+ }
+
+ if (ob->type == OB_MESH || ob->type == OB_MBALL || ob->type == OB_CURVE || ob->type == OB_SURF ||
+ ob->type == OB_FONT) {
+
+ if (ob->type == OB_MESH) {
+ use_mesh = DEG_get_evaluated_object(dg, ob)->data;
+ }
+ else {
+ use_mesh = BKE_mesh_new_from_object(NULL, ob, false);
+ }
+
+ /* In case curve object is empty. */
+ if (!use_mesh) {
+ return;
+ }
+
+ mul_m4db_m4db_m4fl_uniq(new_mvp, mvp_mat, ob->obmat);
+ mul_m4db_m4db_m4fl_uniq(new_mv, mv_mat, ob->obmat);
+
+ invert_m4_m4(imat, ob->obmat);
+ transpose_m4(imat);
+ copy_m4d_m4(normal, imat);
+
+ if (use_mesh->edit_mesh) {
+ bm = BM_mesh_copy(use_mesh->edit_mesh->bm);
+ }
+ else {
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(use_mesh)));
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+ BM_mesh_bm_from_me(bm,
+ use_mesh,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ }
+
+ if (rb->remove_doubles) {
+ BMEditMesh *em = BKE_editmesh_create(bm, false);
+ BMOperator findop, weldop;
+
+ /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */
+ BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001);
+
+ BMO_op_exec(bm, &findop);
+
+ /* weld the vertices. */
+ BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts");
+ BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
+ BMO_op_exec(bm, &weldop);
+
+ BMO_op_finish(bm, &findop);
+ BMO_op_finish(bm, &weldop);
+
+ MEM_freeN(em);
+ }
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false);
+ BM_mesh_triangulate(
+ bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL);
+ BM_mesh_normals_update(bm);
+ BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
+ CanFindFreestyle = 1;
+ }
+
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ LRT_MESH_FINISH
+ return;
+ }
+
+ orv = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderVert) * bm->totvert);
+ ort = lineart_mem_aquire(&rb->render_data_pool, bm->totface * rb->triangle_size);
+
+ orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob;
+
+ reln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers,
+ &rb->render_data_pool,
+ orv,
+ sizeof(LineartRenderElementLinkNode));
+ reln->element_count = bm->totvert;
+ reln->object_ref = orig_ob;
+
+ if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
+ use_crease = cosf(M_PI - ob->lineart.crease_threshold);
+ }
+ else {
+ use_crease = rb->crease_threshold;
+ }
+
+ /* Temp solution for getting clean 2D text, future configuration will allow customizations. */
+ if (ob->type == OB_FONT) {
+ reln->flags |= LRT_ELEMENT_BORDER_ONLY;
+ }
+
+ reln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers,
+ &rb->render_data_pool,
+ ort,
+ sizeof(LineartRenderElementLinkNode));
+ reln->element_count = bm->totface;
+ reln->object_ref = orig_ob;
+ reln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0);
+
+ /* Note this memory is not from pool, will be deleted after culling. */
+ orta = MEM_callocN(sizeof(LineartRenderTriangleAdjacent) * bm->totface,
+ "LineartRenderTriangleAdjacent");
+ /* Link is minimal so we use pool anyway. */
+ lineart_list_append_pointer_pool(&rb->triangle_adjacent_pointers, &rb->render_data_pool, orta);
+
+ for (i = 0; i < bm->totvert; i++) {
+ v = BM_vert_at_index(bm, i);
+ lineart_vert_transform(v, i, orv, new_mv, new_mvp);
+ orv[i].index = i + global_i;
+ }
+ (*global_vindex) += bm->totvert;
+
+ rt = ort;
+ for (i = 0; i < bm->totface; i++) {
+ f = BM_face_at_index(bm, i);
+
+ loop = f->l_first;
+ rt->v[0] = &orv[BM_elem_index_get(loop->v)];
+ loop = loop->next;
+ rt->v[1] = &orv[BM_elem_index_get(loop->v)];
+ loop = loop->next;
+ rt->v[2] = &orv[BM_elem_index_get(loop->v)];
+
+ Material *mat = BKE_object_material_get(ob, f->mat_nr + 1);
+ rt->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ?
+ mat->lineart.transparency_mask :
+ 0);
+
+ double gn[3];
+ copy_v3db_v3fl(gn, f->no);
+ mul_v3_mat3_m4v3_db(rt->gn, normal, gn);
+ normalize_v3_db(rt->gn);
+
+ if (usage == OBJECT_LRT_INTERSECTION_ONLY) {
+ rt->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
+ }
+ else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) {
+ rt->flags |= LRT_TRIANGLE_NO_INTERSECTION;
+ }
+
+ /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
+ rt->intersecting_verts = (void *)&orta[i];
+
+ rt = (LineartRenderTriangle *)(((unsigned char *)rt) + rb->triangle_size);
+ }
+
+ /* use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */
+
+ int allocate_rl = 0;
+ for (i = 0; i < bm->totedge; i++) {
+ e = BM_edge_at_index(bm, i);
+
+ /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */
+ char eflag = lineart_test_feature_line(
+ rb, e, ort, orv, use_crease, ob->type == OB_FONT, CanFindFreestyle, bm);
+ if (eflag) {
+ allocate_rl++;
+ }
+ /* Here we just use bm's flag for convenience, it's deleted after loading anyway. Always set
+ * the flag, so we don't need to clean it. */
+ e->head.hflag = eflag;
+ }
+
+ orl = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLine) * allocate_rl);
+ reln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers,
+ &rb->render_data_pool,
+ orl,
+ sizeof(LineartRenderElementLinkNode));
+ reln->element_count = allocate_rl;
+ reln->object_ref = orig_ob;
+
+ rl = orl;
+ for (i = 0; i < bm->totedge; i++) {
+ e = BM_edge_at_index(bm, i);
+
+ if (!e->head.hflag) {
+ continue;
+ }
+
+ rl->l = &orv[BM_elem_index_get(e->v1)];
+ rl->r = &orv[BM_elem_index_get(e->v2)];
+ rl->l_obindex = rl->l->index - global_i;
+ rl->r_obindex = rl->r->index - global_i;
+ if (e->l) {
+ int findex = BM_elem_index_get(e->l->f);
+ rl->tl = lineart_triangle_from_index(rb, ort, findex);
+ lineart_triangle_adjacent_assign(rl->tl, &orta[findex], rl);
+ if (e->l->radial_next && e->l->radial_next != e->l) {
+ findex = BM_elem_index_get(e->l->radial_next->f);
+ rl->tr = lineart_triangle_from_index(rb, ort, findex);
+ lineart_triangle_adjacent_assign(rl->tr, &orta[findex], rl);
+ }
+ }
+ rl->flags = e->head.hflag;
+ rl->object_ref = orig_ob;
+
+ LineartRenderLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool,
+ sizeof(LineartRenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ if (usage == OBJECT_LRT_INHERENT || usage == OBJECT_LRT_INCLUDE ||
+ usage == OBJECT_LRT_NO_INTERSECTION) {
+ lineart_add_line_to_list(rb, rl);
+ }
+
+ rl++;
+ }
+
+ LRT_MESH_FINISH
+ }
+
+#undef LRT_MESH_FINISH
+}
+
+int ED_lineart_object_collection_usage_check(Collection *c, Object *ob)
+{
+
+ if (!c) {
+ return OBJECT_LRT_INHERENT;
+ }
+
+ int object_is_used = (ob->lineart.usage != OBJECT_LRT_INHERENT);
+
+ if (object_is_used) {
+ return ob->lineart.usage;
+ }
+
+ if (c->children.first == NULL) {
+ if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
+ if (ob->lineart.usage == OBJECT_LRT_INHERENT) {
+ if (c->lineart_usage == COLLECTION_LRT_OCCLUSION_ONLY) {
+ return OBJECT_LRT_OCCLUSION_ONLY;
+ }
+ else if (c->lineart_usage == COLLECTION_LRT_EXCLUDE) {
+ return OBJECT_LRT_EXCLUDE;
+ }
+ else if (c->lineart_usage == COLLECTION_LRT_INTERSECTION_ONLY) {
+ return OBJECT_LRT_INTERSECTION_ONLY;
+ }
+ else if (c->lineart_usage == COLLECTION_LRT_NO_INTERSECTION) {
+ return OBJECT_LRT_NO_INTERSECTION;
+ }
+ else {
+ return OBJECT_LRT_INHERENT;
+ }
+ }
+ else {
+ return ob->lineart.usage;
+ }
+ }
+ else {
+ return OBJECT_LRT_INHERENT;
+ }
+ }
+
+ LISTBASE_FOREACH (CollectionChild *, cc, &c->children) {
+ int result = ED_lineart_object_collection_usage_check(cc->collection, ob);
+ if (result > OBJECT_LRT_INHERENT) {
+ return result;
+ }
+ }
+
+ return OBJECT_LRT_INHERENT;
+}
+
+static void lineart_main_load_geometries(
+ Depsgraph *depsgraph,
+ Scene *scene,
+ Object *camera /* Still use camera arg for convenience. */,
+ LineartRenderBuffer *rb)
+{
+ double proj[4][4], view[4][4], result[4][4];
+ float inv[4][4];
+
+ /* lock becore accessing shared status data. */
+ BLI_spin_lock(&lineart_share.lock_render_status);
+
+ memset(rb->material_pointers, 0, sizeof(void *) * 2048);
+
+ if (lineart_share.viewport_camera_override) {
+ copy_m4d_m4(proj, lineart_share.persp);
+ invert_m4_m4(inv, lineart_share.viewinv);
+ copy_m4_m4_db(rb->view_projection, proj);
+ }
+ else {
+ Camera *cam = camera->data;
+ float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
+ double fov = focallength_to_fov(cam->lens, sensor);
+
+ double asp = ((double)rb->w / (double)rb->h);
+
+ if (cam->type == CAM_PERSP) {
+ if (asp < 1) {
+ fov /= asp;
+ }
+ lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
+ }
+ else if (cam->type == CAM_ORTHO) {
+ 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, rb->cam_obmat);
+ mul_m4db_m4db_m4fl_uniq(result, proj, inv);
+ copy_m4_m4_db(proj, result);
+ copy_m4_m4_db(rb->view_projection, proj);
+ }
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+
+ unit_m4_db(view);
+
+ BLI_listbase_clear(&rb->triangle_buffer_pointers);
+ BLI_listbase_clear(&rb->vertex_buffer_pointers);
+
+ int flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET |
+ DEG_ITER_OBJECT_FLAG_VISIBLE;
+
+ if (scene->lineart.flags & LRT_ALLOW_DUPLI_OBJECTS) {
+ flags |= DEG_ITER_OBJECT_FLAG_DUPLI;
+ }
+
+ int global_i = 0;
+
+ DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) {
+ int usage = ED_lineart_object_collection_usage_check(scene->master_collection, ob);
+
+ lineart_geometry_object_load(depsgraph, ob, view, proj, rb, usage, &global_i);
+
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ return;
+ }
+ }
+ DEG_OBJECT_ITER_END;
+}
+
+static bool lineart_another_edge_2v(const LineartRenderTriangle *rt,
+ const LineartRenderVert *rv,
+ LineartRenderVert **l,
+ LineartRenderVert **r)
+{
+ if (rt->v[0] == rv) {
+ *l = rt->v[1];
+ *r = rt->v[2];
+ return true;
+ }
+ else if (rt->v[1] == rv) {
+ *l = rt->v[2];
+ *r = rt->v[0];
+ return true;
+ }
+ else if (rt->v[2] == rv) {
+ *l = rt->v[0];
+ *r = rt->v[1];
+ return true;
+ }
+ return false;
+}
+
+static int lineart_edge_from_triangle(const LineartRenderTriangle *rt, const LineartRenderLine *rl)
+{
+ if (rl->tl == rt || rl->tr == rt) {
+ return 1;
+ }
+ if (lineart_share.allow_overlapping_edges) {
+#define LRT_TRI_SAME_POINT(rt, i, pt) \
+ ((LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])) || \
+ (LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])))
+ if ((LRT_TRI_SAME_POINT(rt, 0, rl->l) || LRT_TRI_SAME_POINT(rt, 1, rl->l) ||
+ LRT_TRI_SAME_POINT(rt, 2, rl->l)) &&
+ (LRT_TRI_SAME_POINT(rt, 0, rl->r) || LRT_TRI_SAME_POINT(rt, 1, rl->r) ||
+ LRT_TRI_SAME_POINT(rt, 2, rl->r))) {
+ return 1;
+ }
+#undef LRT_TRI_SAME_POINT
+ }
+ return 0;
+}
+
+#define INTERSECT_SORT_MIN_TO_MAX_3(ia, ib, ic, lst) \
+ { \
+ lst[0] = LRT_MIN3_INDEX(ia, ib, ic); \
+ lst[1] = (((ia <= ib && ib <= ic) || (ic <= ib && ib <= ia)) ? \
+ 1 : \
+ (((ic <= ia && ia <= ib) || (ib < ia && ia <= ic)) ? 0 : 2)); \
+ lst[2] = LRT_MAX3_INDEX(ia, ib, ic); \
+ }
+
+/* ia ib ic are ordered. */
+#define INTERSECT_JUST_GREATER(is, order, num, index) \
+ { \
+ index = (num < is[order[0]] ? \
+ order[0] : \
+ (num < is[order[1]] ? order[1] : (num < is[order[2]] ? order[2] : order[2]))); \
+ }
+
+/* ia ib ic are ordered. */
+#define INTERSECT_JUST_SMALLER(is, order, num, index) \
+ { \
+ index = (num > is[order[2]] ? \
+ order[2] : \
+ (num > is[order[1]] ? order[1] : (num > is[order[0]] ? order[0] : order[0]))); \
+ }
+
+/** This is the main function to calculate
+ * the occlusion status between 1(one) triangle and 1(one) line.
+ * if returned 1, then from/to will carry the occludded segments
+ * in ratio from rl->l to rl->r. the line is later cutted with
+ * these two values.
+ */
+static int lineart_triangle_line_imagespace_intersection_v2(SpinLock *UNUSED(spl),
+ const LineartRenderTriangle *rt,
+ const LineartRenderLine *rl,
+ const double *override_cam_loc,
+ const char override_cam_is_persp,
+ const double vp[4][4],
+ const double *camera_dir,
+ const float cam_shift_x,
+ const float cam_shift_y,
+ double *from,
+ double *to)
+{
+ double is[3] = {0};
+ int order[3];
+ int LCross = -1, RCross = -1;
+ int a, b, c;
+ int st_l = 0, st_r = 0;
+
+ double Lv[3];
+ double Rv[3];
+ double vd4[4];
+ double Cv[3];
+ double dot_l, dot_r, dot_la, dot_ra;
+ double dot_f;
+ double gloc[4], trans[4];
+ double cut = -1;
+
+ double *LFBC = rl->l->fbcoord, *RFBC = rl->r->fbcoord, *FBC0 = rt->v[0]->fbcoord,
+ *FBC1 = rt->v[1]->fbcoord, *FBC2 = rt->v[2]->fbcoord;
+
+ /* No potential overlapping, return early. */
+ 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])) ||
+ (MIN3(FBC0[3], FBC1[3], FBC2[3]) > MAX2(LFBC[3], RFBC[3]))) {
+ return 0;
+ }
+
+ /* If the the line is one of the edge in the triangle, then it'scene not occludded. */
+ if (lineart_edge_from_triangle(rt, rl)) {
+ return 0;
+ }
+
+ /* If the line visually crosses one of the edge in the triangle. */
+ a = lineart_LineIntersectTest2d(LFBC, RFBC, FBC0, FBC1, &is[0]);
+ b = lineart_LineIntersectTest2d(LFBC, RFBC, FBC1, FBC2, &is[1]);
+ c = lineart_LineIntersectTest2d(LFBC, RFBC, FBC2, FBC0, &is[2]);
+
+ INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order);
+
+ sub_v3_v3v3_db(Lv, rl->l->gloc, rt->v[0]->gloc);
+ sub_v3_v3v3_db(Rv, rl->r->gloc, rt->v[0]->gloc);
+
+ copy_v3_v3_db(Cv, camera_dir);
+
+ if (override_cam_is_persp) {
+ copy_v3_v3_db(vd4, override_cam_loc);
+ }
+ else {
+ copy_v4_v4_db(vd4, override_cam_loc);
+ }
+ if (override_cam_is_persp) {
+ sub_v3_v3v3_db(Cv, vd4, rt->v[0]->gloc);
+ }
+
+ dot_l = dot_v3v3_db(Lv, rt->gn);
+ dot_r = dot_v3v3_db(Rv, rt->gn);
+ dot_f = dot_v3v3_db(Cv, rt->gn);
+
+ if (!dot_f) {
+ return 0;
+ }
+
+ if (!a && !b && !c) {
+ if (!(st_l = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2)) &&
+ !(st_r = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2))) {
+ return 0; /* not occluding. */
+ }
+ }
+
+ st_l = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2);
+ st_r = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2);
+
+ dot_la = fabs(dot_l);
+ if (dot_la < DBL_EPSILON) {
+ dot_la = 0;
+ dot_l = 0;
+ }
+ dot_ra = fabs(dot_r);
+ if (dot_ra < DBL_EPSILON) {
+ dot_ra = 0;
+ dot_r = 0;
+ }
+ if (dot_l - dot_r == 0) {
+ cut = 100000;
+ }
+ else if (dot_l * dot_r <= 0) {
+ cut = dot_la / fabs(dot_l - dot_r);
+ }
+ else {
+ cut = fabs(dot_r + dot_l) / fabs(dot_l - dot_r);
+ cut = dot_ra > dot_la ? 1 - cut : cut;
+ }
+
+ if (override_cam_is_persp) {
+ interp_v3_v3v3_db(gloc, rl->l->gloc, rl->r->gloc, cut);
+ mul_v4_m4v3_db(trans, vp, gloc);
+ mul_v3db_db(trans, (1 / trans[3]));
+ }
+ else {
+ interp_v3_v3v3_db(trans, rl->l->fbcoord, rl->r->fbcoord, cut);
+ }
+ trans[0] -= cam_shift_x * 2;
+ trans[1] -= cam_shift_y * 2;
+
+ /* To accomodate k=0 and k=inf (vertical) lines. */
+ if (fabs(rl->l->fbcoord[0] - rl->r->fbcoord[0]) > fabs(rl->l->fbcoord[1] - rl->r->fbcoord[1])) {
+ cut = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], trans[0]);
+ }
+ else {
+ cut = ratiod(rl->l->fbcoord[1], rl->r->fbcoord[1], trans[1]);
+ }
+
+ if (st_l == 2) {
+ if (st_r == 2) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 1) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 0) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 0, RCross);
+ }
+ }
+ else if (st_l == 1) {
+ if (st_r == 2) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 1) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 0) {
+ INTERSECT_JUST_GREATER(is, order, DBL_TRIANGLE_LIM, RCross);
+ if (LRT_ABC(RCross) && is[RCross] > (DBL_TRIANGLE_LIM)) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ }
+ else {
+ INTERSECT_JUST_SMALLER(is, order, -DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, -DBL_TRIANGLE_LIM, RCross);
+ }
+ }
+ }
+ else if (st_l == 0) {
+ if (st_r == 2) {
+ INTERSECT_JUST_SMALLER(is, order, 1 - DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 1) {
+ INTERSECT_JUST_SMALLER(is, order, 1 - DBL_TRIANGLE_LIM, LCross);
+ if (LRT_ABC(LCross) && is[LCross] < (1 - DBL_TRIANGLE_LIM)) {
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else {
+ INTERSECT_JUST_SMALLER(is, order, 1 + DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 + DBL_TRIANGLE_LIM, RCross);
+ }
+ }
+ else if (st_r == 0) {
+ INTERSECT_JUST_GREATER(is, order, 0, LCross);
+ if (LRT_ABC(LCross) && is[LCross] > 0) {
+ INTERSECT_JUST_GREATER(is, order, is[LCross], RCross);
+ }
+ else {
+ INTERSECT_JUST_GREATER(is, order, is[LCross], LCross);
+ INTERSECT_JUST_GREATER(is, order, is[LCross], RCross);
+ }
+ }
+ }
+
+ double LF = dot_l * dot_f, RF = dot_r * dot_f;
+
+ if (LF <= 0 && RF <= 0 && (dot_l || dot_r)) {
+
+ *from = MAX2(0, is[LCross]);
+ *to = MIN2(1, is[RCross]);
+ if (*from >= *to) {
+ return 0;
+ }
+ /* printf("1 From %f to %f\n",*From, *To);. */
+ return 1;
+ }
+ else if (LF >= 0 && RF <= 0 && (dot_l || dot_r)) {
+ *from = MAX2(cut, is[LCross]);
+ *to = MIN2(1, is[RCross]);
+ if (*from >= *to) {
+ return 0;
+ }
+ /* printf("2 From %f to %f\n",*From, *To);. */
+ return 1;
+ }
+ else if (LF <= 0 && RF >= 0 && (dot_l || dot_r)) {
+ *from = MAX2(0, is[LCross]);
+ *to = MIN2(cut, is[RCross]);
+ if (*from >= *to) {
+ return 0;
+ }
+ /* printf("3 From %f to %f\n",*From, *To);. */
+ return 1;
+ }
+ else
+ return 0;
+ return 1;
+}
+
+#undef INTERSECT_SORT_MIN_TO_MAX_3
+#undef INTERSECT_JUST_GREATER
+#undef INTERSECT_JUST_SMALLER
+
+static bool lineart_triangle_share_edge(const LineartRenderTriangle *l,
+ const LineartRenderTriangle *r)
+{
+ if (l->v[0]->index == r->v[0]->index) {
+ if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[2]->index ||
+ l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[1]->index) {
+ return true;
+ }
+ }
+ if (l->v[0]->index == r->v[1]->index) {
+ if (l->v[1]->index == r->v[0]->index || l->v[1]->index == r->v[2]->index ||
+ l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[0]->index) {
+ return true;
+ }
+ }
+ if (l->v[0]->index == r->v[2]->index) {
+ if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[0]->index ||
+ l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[1]->index) {
+ return true;
+ }
+ }
+ if (l->v[1]->index == r->v[0]->index) {
+ if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[2]->index ||
+ l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[1]->index) {
+ return true;
+ }
+ }
+ if (l->v[1]->index == r->v[1]->index) {
+ if (l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[2]->index ||
+ l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[0]->index) {
+ return true;
+ }
+ }
+ if (l->v[1]->index == r->v[2]->index) {
+ if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[0]->index ||
+ l->v[0]->index == r->v[0]->index || l->v[0]->index == r->v[1]->index) {
+ return true;
+ }
+ }
+
+ /* otherwise not possible. */
+ return false;
+}
+
+static LineartRenderVert *lineart_triangle_share_point(const LineartRenderTriangle *l,
+ const LineartRenderTriangle *r)
+{
+ if (l->v[0] == r->v[0]) {
+ return r->v[0];
+ }
+ if (l->v[0] == r->v[1]) {
+ return r->v[1];
+ }
+ if (l->v[0] == r->v[2]) {
+ return r->v[2];
+ }
+ if (l->v[1] == r->v[0]) {
+ return r->v[0];
+ }
+ if (l->v[1] == r->v[1]) {
+ return r->v[1];
+ }
+ if (l->v[1] == r->v[2]) {
+ return r->v[2];
+ }
+ if (l->v[2] == r->v[0]) {
+ return r->v[0];
+ }
+ if (l->v[2] == r->v[1]) {
+ return r->v[1];
+ }
+ if (l->v[2] == r->v[2]) {
+ return r->v[2];
+ }
+ return NULL;
+}
+
+static bool lineart_vert_already_intersected_2v(LineartRenderVertIntersection *rv,
+ LineartRenderVertIntersection *v1,
+ LineartRenderVertIntersection *v2)
+{
+ return ((rv->isec1 == v1->base.index && rv->isec2 == v2->base.index) ||
+ (rv->isec2 == v2->base.index && rv->isec1 == v1->base.index));
+}
+
+static void lineart_vert_set_intersection_2v(LineartRenderVert *rv,
+ LineartRenderVert *v1,
+ LineartRenderVert *v2)
+{
+ LineartRenderVertIntersection *irv = (LineartRenderVertIntersection *)rv;
+ irv->isec1 = v1->index;
+ irv->isec2 = v2->index;
+}
+
+static LineartRenderVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *rb,
+ LineartRenderVert *v1,
+ LineartRenderVert *v2,
+ LineartRenderTriangle *rt,
+ LineartRenderTriangle *testing,
+ LineartRenderVert *last)
+{
+ double Lv[3];
+ double Rv[3];
+ double dot_l, dot_r;
+ LineartRenderVert *result;
+ double gloc[3];
+ LineartRenderVert *l = v1, *r = v2;
+
+ for (LinkNode *ln = (void *)testing->intersecting_verts; ln; ln = ln->next) {
+ LineartRenderVertIntersection *rv = ln->link;
+ if (rv->intersecting_with == rt &&
+ lineart_vert_already_intersected_2v(
+ rv, (LineartRenderVertIntersection *)l, (LineartRenderVertIntersection *)r)) {
+ return (LineartRenderVert *)rv;
+ }
+ }
+
+ sub_v3_v3v3_db(Lv, l->gloc, testing->v[0]->gloc);
+ sub_v3_v3v3_db(Rv, r->gloc, testing->v[0]->gloc);
+
+ dot_l = dot_v3v3_db(Lv, testing->gn);
+ dot_r = dot_v3v3_db(Rv, testing->gn);
+
+ if (dot_l * dot_r > 0 || (!dot_l && !dot_r)) {
+ return 0;
+ }
+
+ dot_l = fabs(dot_l);
+ dot_r = fabs(dot_r);
+
+ interp_v3_v3v3_db(gloc, l->gloc, r->gloc, dot_l / (dot_l + dot_r));
+
+ if (last && LRT_DOUBLE_CLOSE_ENOUGH(last->gloc[0], gloc[0]) &&
+ LRT_DOUBLE_CLOSE_ENOUGH(last->gloc[1], gloc[1]) &&
+ LRT_DOUBLE_CLOSE_ENOUGH(last->gloc[2], gloc[2])) {
+ return NULL;
+ }
+
+ if (!(lineart_point_inside_triangle3de(
+ gloc, testing->v[0]->gloc, testing->v[1]->gloc, testing->v[2]->gloc))) {
+ return NULL;
+ }
+
+ result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderVertIntersection));
+
+ result->flag = LRT_VERT_HAS_INTERSECTION_DATA;
+
+ copy_v3_v3_db(result->gloc, gloc);
+
+ lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, result);
+
+ return result;
+}
+
+static LineartRenderLine *lineart_triangle_generate_intersection_line_only(
+ LineartRenderBuffer *rb, LineartRenderTriangle *rt, LineartRenderTriangle *testing)
+{
+ LineartRenderVert *l = 0, *r = 0;
+ LineartRenderVert **next = &l;
+ LineartRenderLine *result;
+ LineartRenderVert *E0T = 0;
+ LineartRenderVert *E1T = 0;
+ LineartRenderVert *E2T = 0;
+ LineartRenderVert *TE0 = 0;
+ LineartRenderVert *TE1 = 0;
+ LineartRenderVert *TE2 = 0;
+ LineartRenderVert *sv1, *sv2;
+ double cl[3];
+
+ double ZMin, ZMax;
+ ZMax = rb->far_clip;
+ ZMin = rb->near_clip;
+ copy_v3_v3_db(cl, rb->camera_pos);
+ LineartRenderVert *share = lineart_triangle_share_point(testing, rt);
+
+ if (share) {
+ LineartRenderVert *new_share;
+ lineart_another_edge_2v(rt, share, &sv1, &sv2);
+
+ l = new_share = lineart_mem_aquire(&rb->render_data_pool,
+ (sizeof(LineartRenderVertIntersection)));
+
+ new_share->flag = LRT_VERT_HAS_INTERSECTION_DATA;
+
+ copy_v3_v3_db(new_share->gloc, share->gloc);
+
+ r = lineart_triangle_2v_intersection_test(rb, sv1, sv2, rt, testing, 0);
+
+ if (r == NULL) {
+ lineart_another_edge_2v(testing, share, &sv1, &sv2);
+ r = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, rt, 0);
+ if (r == NULL) {
+ return 0;
+ }
+ lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, new_share);
+ }
+ else {
+ lineart_prepend_pool(&rt->intersecting_verts, &rb->render_data_pool, new_share);
+ }
+ }
+ else {
+ E0T = lineart_triangle_2v_intersection_test(rb, rt->v[0], rt->v[1], rt, testing, 0);
+ if (E0T && (!(*next))) {
+ (*next) = E0T;
+ lineart_vert_set_intersection_2v((*next), rt->v[0], rt->v[1]);
+ next = &r;
+ }
+ E1T = lineart_triangle_2v_intersection_test(rb, rt->v[1], rt->v[2], rt, testing, l);
+ if (E1T && (!(*next))) {
+ (*next) = E1T;
+ lineart_vert_set_intersection_2v((*next), rt->v[1], rt->v[2]);
+ next = &r;
+ }
+ if (!(*next)) {
+ E2T = lineart_triangle_2v_intersection_test(rb, rt->v[2], rt->v[0], rt, testing, l);
+ }
+ if (E2T && (!(*next))) {
+ (*next) = E2T;
+ lineart_vert_set_intersection_2v((*next), rt->v[2], rt->v[0]);
+ next = &r;
+ }
+
+ if (!(*next)) {
+ TE0 = lineart_triangle_2v_intersection_test(
+ rb, testing->v[0], testing->v[1], testing, rt, l);
+ }
+ if (TE0 && (!(*next))) {
+ (*next) = TE0;
+ lineart_vert_set_intersection_2v((*next), testing->v[0], testing->v[1]);
+ next = &r;
+ }
+ if (!(*next)) {
+ TE1 = lineart_triangle_2v_intersection_test(
+ rb, testing->v[1], testing->v[2], testing, rt, l);
+ }
+ if (TE1 && (!(*next))) {
+ (*next) = TE1;
+ lineart_vert_set_intersection_2v((*next), testing->v[1], testing->v[2]);
+ next = &r;
+ }
+ if (!(*next)) {
+ TE2 = lineart_triangle_2v_intersection_test(
+ rb, testing->v[2], testing->v[0], testing, rt, l);
+ }
+ if (TE2 && (!(*next))) {
+ (*next) = TE2;
+ lineart_vert_set_intersection_2v((*next), testing->v[2], testing->v[0]);
+ next = &r;
+ }
+
+ if (!(*next)) {
+ return 0;
+ }
+ }
+ mul_v4_m4v3_db(l->fbcoord, rb->view_projection, l->gloc);
+ mul_v4_m4v3_db(r->fbcoord, rb->view_projection, r->gloc);
+ mul_v3db_db(l->fbcoord, (1 / l->fbcoord[3]));
+ mul_v3db_db(r->fbcoord, (1 / r->fbcoord[3]));
+
+ l->fbcoord[0] -= rb->shift_x * 2;
+ l->fbcoord[1] -= rb->shift_y * 2;
+ r->fbcoord[0] -= rb->shift_x * 2;
+ r->fbcoord[1] -= rb->shift_y * 2;
+
+ /* This z transformation is not the same as the rest of the part, because the data don't go
+ * through normal perspective division calls in the pipeline, but this way the 3D result and
+ * occlution on the generated line is correct, and we don't really use 2D for viewport stroke
+ * generation anyway.*/
+ l->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(l->fbcoord[2]) * (ZMax - ZMin));
+ r->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(r->fbcoord[2]) * (ZMax - ZMin));
+
+ ((LineartRenderVertIntersection *)l)->intersecting_with = rt;
+ ((LineartRenderVertIntersection *)r)->intersecting_with = testing;
+
+ result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLine));
+ result->l = l;
+ result->r = r;
+ result->tl = rt;
+ result->tr = testing;
+
+ /** TODO: Use bit flags similar to transparency_mask to select intersection pairs, or to use rt
+ * address as rt's are continuously allocated in memory for each object. object_ref is removed.
+ */
+ /* result->object_ref = rt->object_ref;. */
+
+ LineartRenderLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool,
+ sizeof(LineartRenderLineSegment));
+ BLI_addtail(&result->segments, rls);
+ /* Don't need to OR flags right now, just a type mark. */
+ result->flags = LRT_EDGE_FLAG_INTERSECTION;
+ lineart_prepend_line_direct(&rb->intersection_lines, result);
+ int r1, r2, c1, c2, row, col;
+ if (lineart_get_line_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) {
+ for (row = r1; row != r2 + 1; row++) {
+ for (col = c1; col != c2 + 1; col++) {
+ lineart_bounding_area_link_line(rb, &rb->initial_bounding_areas[row * 4 + col], result);
+ }
+ }
+ }
+
+ rb->intersection_count++;
+
+ return result;
+}
+
+static void lineart_triangle_intersections_in_bounding_area(LineartRenderBuffer *rb,
+ LineartRenderTriangle *rt,
+ LineartBoundingArea *ba)
+{
+ /* testing_triangle->testing[0] is used to store pairing triangle reference.
+ * See definition of LineartRenderTriangleThread for more info. */
+ LineartRenderTriangle *testing_triangle;
+ LineartRenderTriangleThread *rtt;
+ LinkData *lip, *next_lip;
+
+ double *G0 = rt->v[0]->gloc, *G1 = rt->v[1]->gloc, *G2 = rt->v[2]->gloc;
+
+ if (ba->child) {
+ lineart_triangle_intersections_in_bounding_area(rb, rt, &ba->child[0]);
+ lineart_triangle_intersections_in_bounding_area(rb, rt, &ba->child[1]);
+ lineart_triangle_intersections_in_bounding_area(rb, rt, &ba->child[2]);
+ lineart_triangle_intersections_in_bounding_area(rb, rt, &ba->child[3]);
+ return;
+ }
+
+ for (lip = ba->linked_triangles.first; lip; lip = next_lip) {
+ next_lip = lip->next;
+ testing_triangle = lip->data;
+ rtt = (LineartRenderTriangleThread *)testing_triangle;
+
+ /* Note: No self intersect because these triangles doesn't take part in occlusion. */
+
+ if (testing_triangle == rt || rtt->testing[0] == (LineartRenderLine *)rt ||
+ (testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) ||
+ ((testing_triangle->flags & LRT_TRIANGLE_INTERSECTION_ONLY) &&
+ (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY)) ||
+ lineart_triangle_share_edge(rt, testing_triangle)) {
+ continue;
+ }
+
+ rtt->testing[0] = (LineartRenderLine *)rt;
+ double *RG0 = testing_triangle->v[0]->gloc, *RG1 = testing_triangle->v[1]->gloc,
+ *RG2 = testing_triangle->v[2]->gloc;
+
+ if ((MIN3(G0[2], G1[2], G2[2]) > MAX3(RG0[2], RG1[2], RG2[2])) ||
+ (MAX3(G0[2], G1[2], G2[2]) < MIN3(RG0[2], RG1[2], RG2[2])) ||
+ (MIN3(G0[0], G1[0], G2[0]) > MAX3(RG0[0], RG1[0], RG2[0])) ||
+ (MAX3(G0[0], G1[0], G2[0]) < MIN3(RG0[0], RG1[0], RG2[0])) ||
+ (MIN3(G0[1], G1[1], G2[1]) > MAX3(RG0[1], RG1[1], RG2[1])) ||
+ (MAX3(G0[1], G1[1], G2[1]) < MIN3(RG0[1], RG1[1], RG2[1]))) {
+ continue;
+ }
+
+ lineart_triangle_generate_intersection_line_only(rb, rt, testing_triangle);
+ }
+}
+
+static void lineart_main_get_view_vector(LineartRenderBuffer *rb)
+{
+ float direction[3] = {0, 0, 1};
+ float trans[3];
+ float inv[4][4];
+ float obmat_no_scale[4][4];
+
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ if (lineart_share.viewport_camera_override) {
+ if (lineart_share.camera_is_persp) {
+ invert_m4_m4(inv, lineart_share.viewinv);
+ }
+ else {
+ quat_to_mat4(inv, lineart_share.viewquat);
+ }
+ }
+ else {
+ copy_m4_m4(obmat_no_scale, rb->cam_obmat);
+ }
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+ 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(rb->cam_obmat, obmat_no_scale);
+ copy_v3db_v3fl(rb->view_vector, trans);
+}
+
+/* Buffer operations. */
+
+static void lineart_destroy_render_data(void)
+{
+ LineartRenderBuffer *rb = lineart_share.render_buffer_shared;
+ if (rb == NULL) {
+ return;
+ }
+
+ rb->contour_count = 0;
+ rb->contour_managed = NULL;
+ rb->intersection_count = 0;
+ rb->intersection_managed = NULL;
+ rb->material_line_count = 0;
+ rb->material_managed = NULL;
+ rb->crease_count = 0;
+ rb->crease_managed = NULL;
+ rb->edge_mark_count = 0;
+ rb->edge_mark_managed = NULL;
+
+ rb->contours = NULL;
+ rb->intersection_lines = NULL;
+ rb->crease_lines = NULL;
+ rb->material_lines = NULL;
+ rb->edge_marks = NULL;
+
+ BLI_listbase_clear(&rb->chains);
+ BLI_listbase_clear(&rb->wasted_cuts);
+
+ BLI_listbase_clear(&rb->vertex_buffer_pointers);
+ BLI_listbase_clear(&rb->line_buffer_pointers);
+ BLI_listbase_clear(&rb->triangle_buffer_pointers);
+
+ BLI_spin_end(&rb->lock_task);
+ BLI_spin_end(&rb->lock_cuts);
+ BLI_spin_end(&rb->render_data_pool.lock_mem);
+
+ lineart_mem_destroy(&rb->render_data_pool);
+}
+
+void ED_lineart_destroy_render_data(void)
+{
+
+ lineart_destroy_render_data();
+ LineartRenderBuffer *rb = lineart_share.render_buffer_shared;
+ if (rb) {
+ MEM_freeN(rb);
+ lineart_share.render_buffer_shared = NULL;
+ }
+
+ if (G.debug_value == 4000) {
+ printf("LRT: Destroyed render data.\n");
+ }
+}
+
+void ED_lineart_destroy_render_data_external(void)
+{
+ if (!lineart_share.init_complete) {
+ return;
+ }
+
+ while (ED_lineart_calculation_flag_check(LRT_RENDER_RUNNING)) {
+ /* Wait to finish, XXX: should cancel here. */
+ }
+
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ TaskPool *tp_read = lineart_share.background_render_task;
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+
+ if (tp_read) {
+ BLI_task_pool_work_and_wait(lineart_share.background_render_task);
+ BLI_task_pool_free(lineart_share.background_render_task);
+ lineart_share.background_render_task = NULL;
+ }
+
+ ED_lineart_destroy_render_data();
+}
+
+LineartRenderBuffer *ED_lineart_create_render_buffer(Scene *scene)
+{
+ /* Re-init render_buffer_shared. */
+ if (lineart_share.render_buffer_shared) {
+ if (G.debug_value == 4000) {
+ printf("LRT: **** Destroy on create.\n");
+ }
+ while (ED_lineart_modifier_sync_flag_check(LRT_SYNC_CLEARING)) {
+ /* Don't race the clearing stage. */
+ }
+ ED_lineart_destroy_render_data_external();
+ }
+
+ LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer");
+
+ lineart_share.render_buffer_shared = rb;
+ if (lineart_share.viewport_camera_override) {
+ copy_v3db_v3fl(rb->camera_pos, lineart_share.camera_pos);
+ rb->cam_is_persp = lineart_share.camera_is_persp;
+ rb->near_clip = lineart_share.near_clip;
+ rb->far_clip = lineart_share.far_clip;
+ rb->shift_x = rb->shift_y = 0.0f;
+ }
+ else {
+ if (!scene || !scene->camera) {
+ return NULL;
+ }
+ Camera *c = scene->camera->data;
+ double clipping_offset = 0;
+ if (scene->lineart.flags & LRT_ALLOW_CLIPPING_BOUNDARIES) {
+ clipping_offset = 0.0001;
+ }
+ copy_v3db_v3fl(rb->camera_pos, scene->camera->obmat[3]);
+ copy_m4_m4(rb->cam_obmat, scene->camera->obmat);
+ rb->cam_is_persp = (c->type == CAM_PERSP);
+ rb->near_clip = c->clip_start + clipping_offset;
+ rb->far_clip = c->clip_end - clipping_offset;
+ rb->w = scene->r.xsch;
+ rb->h = scene->r.ysch;
+
+ double asp = ((double)rb->w / (double)rb->h);
+ rb->shift_x = (asp >= 1) ? c->shiftx : c->shiftx * asp;
+ rb->shift_y = (asp <= 1) ? c->shifty : c->shifty * asp;
+ }
+
+ rb->crease_threshold = cos(M_PI - scene->lineart.crease_threshold);
+ rb->angle_splitting_threshold = scene->lineart.angle_splitting_threshold;
+ rb->chaining_image_threshold = scene->lineart.chaining_image_threshold;
+ rb->chaining_geometry_threshold = scene->lineart.chaining_geometry_threshold;
+
+ rb->fuzzy_intersections = (scene->lineart.flags & LRT_INTERSECTION_AS_CONTOUR) != 0;
+ rb->fuzzy_everything = (scene->lineart.flags & LRT_EVERYTHING_AS_CONTOUR) != 0;
+ rb->allow_boundaries = (scene->lineart.flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0;
+ rb->remove_doubles = (scene->lineart.flags & LRT_REMOVE_DOUBLES) != 0;
+
+ rb->use_contour = (scene->lineart.line_types & LRT_EDGE_FLAG_CONTOUR) != 0;
+ rb->use_crease = (scene->lineart.line_types & LRT_EDGE_FLAG_CREASE) != 0;
+ rb->use_material = (scene->lineart.line_types & LRT_EDGE_FLAG_MATERIAL) != 0;
+ rb->use_edge_marks = (scene->lineart.line_types & LRT_EDGE_FLAG_EDGE_MARK) != 0;
+ rb->use_intersections = (scene->lineart.line_types & LRT_EDGE_FLAG_INTERSECTION) != 0;
+
+ BLI_spin_init(&rb->lock_task);
+ BLI_spin_init(&rb->lock_cuts);
+ BLI_spin_init(&rb->render_data_pool.lock_mem);
+
+ lineart_share.allow_overlapping_edges = (scene->lineart.flags & LRT_ALLOW_OVERLAPPING_EDGES) !=
+ 0;
+
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ lineart_share.background_render_task = lineart_share.pending_render_task;
+ lineart_share.pending_render_task = NULL;
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+
+ return rb;
+}
+
+static void lineart_post_frame_update_render(struct Main *UNUSED(main),
+ struct PointerRNA **UNUSED(prna),
+ const int UNUSED(num_pointers),
+ void *UNUSED(arg))
+{
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_IDLE, false);
+}
+static bCallbackFuncStore lineart_pre_frame_update = {
+ NULL,
+ NULL, /* next, prev. */
+ lineart_post_frame_update_render, /* func. */
+ NULL, /* arg. */
+ 0 /* alloc. */
+};
+
+void ED_lineart_init_locks()
+{
+ if (!(lineart_share.init_complete & LRT_INIT_LOCKS)) {
+ BLI_spin_init(&lineart_share.lock_loader);
+ BLI_spin_init(&lineart_share.lock_render_status);
+ lineart_share.init_complete |= LRT_INIT_LOCKS;
+
+ BKE_callback_add(&lineart_pre_frame_update, BKE_CB_EVT_FRAME_CHANGE_PRE);
+ }
+}
+
+void ED_lineart_calculation_flag_set(eLineartRenderStatus flag)
+{
+ BLI_spin_lock(&lineart_share.lock_render_status);
+
+ if (flag == LRT_RENDER_FINISHED && lineart_share.flag_render_status == LRT_RENDER_INCOMPELTE) {
+ ; /* Don't set the finished flag when it'scene canceled from any one of the thread.*/
+ }
+ else {
+ lineart_share.flag_render_status = flag;
+ }
+
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+}
+
+bool ED_lineart_calculation_flag_check(eLineartRenderStatus flag)
+{
+ bool match;
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ match = (lineart_share.flag_render_status == flag);
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+ return match;
+}
+
+void ED_lineart_modifier_sync_flag_set(eLineartModifierSyncStatus flag,
+ bool UNUSED(is_from_modifier))
+{
+ BLI_spin_lock(&lineart_share.lock_render_status);
+
+ if (flag != LRT_SYNC_IDLE) {
+ while (lineart_share.flag_sync_staus == LRT_SYNC_CLEARING) {
+ /* Waiting, no double clearing in a new call. */
+ }
+ }
+
+ lineart_share.flag_sync_staus = flag;
+
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+}
+
+bool ED_lineart_modifier_sync_flag_check(eLineartModifierSyncStatus flag)
+{
+ bool match;
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ match = (lineart_share.flag_sync_staus == flag);
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+ return match;
+}
+
+void ED_lineart_modifier_sync_add_customer()
+{
+ BLI_spin_lock(&lineart_share.lock_render_status);
+
+ lineart_share.customers++;
+
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+}
+void ED_lineart_modifier_sync_remove_customer()
+{
+ BLI_spin_lock(&lineart_share.lock_render_status);
+
+ lineart_share.customers--;
+
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+}
+
+bool ED_lineart_modifier_sync_still_has_customer()
+{
+ BLI_spin_lock(&lineart_share.lock_render_status);
+
+ bool non_zero = lineart_share.customers != 0;
+
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+
+ return non_zero;
+}
+
+static int lineart_occlusion_get_max_level(Depsgraph *dg)
+{
+ LineartGpencilModifierData *lmd;
+ int max_occ = 0;
+ int max;
+ int mode = DEG_get_mode(dg);
+
+ DEG_OBJECT_ITER_BEGIN (dg,
+ ob,
+ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) {
+ if (ob->type == OB_GPENCIL) {
+ Object *use_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob;
+ LISTBASE_FOREACH (GpencilModifierData *, md, &use_ob->greasepencil_modifiers) {
+ if (md->type == eGpencilModifierType_Lineart) {
+ if (mode == DAG_EVAL_RENDER) {
+ if (!(md->mode & eGpencilModifierMode_Render)) {
+ continue;
+ }
+ }
+ else {
+ if (!(md->mode & eGpencilModifierMode_Realtime)) {
+ continue;
+ }
+ }
+ lmd = (LineartGpencilModifierData *)md;
+ max = MAX2(lmd->level_start, lmd->level_end);
+ max_occ = MAX2(max, max_occ);
+ }
+ }
+ }
+ }
+ DEG_OBJECT_ITER_END;
+
+ return max_occ;
+}
+
+static int lineart_triangle_size_get(const Scene *scene)
+{
+ if (lineart_share.thread_count == 0) {
+ lineart_share.thread_count = BKE_render_num_threads(&scene->r);
+ }
+ return sizeof(LineartRenderTriangle) +
+ (sizeof(LineartRenderLine *) * lineart_share.thread_count);
+}
+
+static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb)
+{
+ int sp_w = 4; /* 20;. */
+ int sp_h = 4; /* rb->H / (rb->W / sp_w);. */
+ int row, col;
+ LineartBoundingArea *ba;
+ double span_w = (double)1 / sp_w * 2.0;
+ double span_h = (double)1 / sp_h * 2.0;
+
+ rb->tile_count_x = sp_w;
+ rb->tile_count_y = sp_h;
+ rb->width_per_tile = span_w;
+ rb->height_per_tile = span_h;
+
+ rb->bounding_area_count = sp_w * sp_h;
+ rb->initial_bounding_areas = lineart_mem_aquire(
+ &rb->render_data_pool, sizeof(LineartBoundingArea) * rb->bounding_area_count);
+
+ for (row = 0; row < sp_h; row++) {
+ for (col = 0; col < sp_w; col++) {
+ ba = &rb->initial_bounding_areas[row * 4 + col];
+
+ ba->l = span_w * col - 1.0;
+ ba->r = (col == sp_w - 1) ? 1.0 : (span_w * (col + 1) - 1.0);
+ ba->u = 1.0 - span_h * row;
+ ba->b = (row == sp_h - 1) ? -1.0 : (1.0 - span_h * (row + 1));
+
+ ba->cx = (ba->l + ba->r) / 2;
+ ba->cy = (ba->u + ba->b) / 2;
+
+ if (row) {
+ lineart_list_append_pointer_pool(
+ &ba->up, &rb->render_data_pool, &rb->initial_bounding_areas[(row - 1) * 4 + col]);
+ }
+ if (col) {
+ lineart_list_append_pointer_pool(
+ &ba->lp, &rb->render_data_pool, &rb->initial_bounding_areas[row * 4 + col - 1]);
+ }
+ if (row != sp_h - 1) {
+ lineart_list_append_pointer_pool(
+ &ba->bp, &rb->render_data_pool, &rb->initial_bounding_areas[(row + 1) * 4 + col]);
+ }
+ if (col != sp_w - 1) {
+ lineart_list_append_pointer_pool(
+ &ba->rp, &rb->render_data_pool, &rb->initial_bounding_areas[row * 4 + col + 1]);
+ }
+ }
+ }
+}
+
+static void lineart_bounding_areas_connect_new(LineartRenderBuffer *rb, LineartBoundingArea *root)
+{
+ LineartBoundingArea *ba = root->child, *tba;
+ LinkData *lip2, *next_lip;
+ LineartStaticMemPool *mph = &rb->render_data_pool;
+
+ /* Inter-connection with newly created 4 child bounding areas. */
+ lineart_list_append_pointer_pool(&ba[1].rp, mph, &ba[0]);
+ lineart_list_append_pointer_pool(&ba[0].lp, mph, &ba[1]);
+ lineart_list_append_pointer_pool(&ba[1].bp, mph, &ba[2]);
+ lineart_list_append_pointer_pool(&ba[2].up, mph, &ba[1]);
+ lineart_list_append_pointer_pool(&ba[2].rp, mph, &ba[3]);
+ lineart_list_append_pointer_pool(&ba[3].lp, mph, &ba[2]);
+ lineart_list_append_pointer_pool(&ba[3].up, mph, &ba[0]);
+ lineart_list_append_pointer_pool(&ba[0].bp, mph, &ba[3]);
+
+ /** Connect 4 child bounding areas to other areas that are
+ * adjacent to their original parents. */
+ LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
+
+ /** For example, we are dealing with parent'scene left side
+ * tba represents each adjacent neighbor of the parent.
+ . */
+ tba = lip->data;
+
+ /** if this neighbor is adjacent to
+ * the two new areas on the left side of the parent,
+ * then add them to the adjacent list as well. */
+ if (ba[1].u > tba->b && ba[1].b < tba->u) {
+ lineart_list_append_pointer_pool(&ba[1].lp, mph, tba);
+ lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
+ }
+ if (ba[2].u > tba->b && ba[2].b < tba->u) {
+ lineart_list_append_pointer_pool(&ba[2].lp, mph, tba);
+ lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
+ }
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
+ tba = lip->data;
+ if (ba[0].u > tba->b && ba[0].b < tba->u) {
+ lineart_list_append_pointer_pool(&ba[0].rp, mph, tba);
+ lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
+ }
+ if (ba[3].u > tba->b && ba[3].b < tba->u) {
+ lineart_list_append_pointer_pool(&ba[3].rp, mph, tba);
+ lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
+ }
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &root->up) {
+ tba = lip->data;
+ if (ba[0].r > tba->l && ba[0].l < tba->r) {
+ lineart_list_append_pointer_pool(&ba[0].up, mph, tba);
+ lineart_list_append_pointer_pool(&tba->bp, mph, &ba[0]);
+ }
+ if (ba[1].r > tba->l && ba[1].l < tba->r) {
+ lineart_list_append_pointer_pool(&ba[1].up, mph, tba);
+ lineart_list_append_pointer_pool(&tba->bp, mph, &ba[1]);
+ }
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
+ tba = lip->data;
+ if (ba[2].r > tba->l && ba[2].l < tba->r) {
+ lineart_list_append_pointer_pool(&ba[2].bp, mph, tba);
+ lineart_list_append_pointer_pool(&tba->up, mph, &ba[2]);
+ }
+ if (ba[3].r > tba->l && ba[3].l < tba->r) {
+ lineart_list_append_pointer_pool(&ba[3].bp, mph, tba);
+ lineart_list_append_pointer_pool(&tba->up, mph, &ba[3]);
+ }
+ }
+
+ /** Then remove the parent bounding areas from
+ * their original adjacent areas. */
+ LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
+ for (lip2 = ((LineartBoundingArea *)lip->data)->rp.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ lineart_list_remove_pointer_item_no_free(&((LineartBoundingArea *)lip->data)->rp, lip2);
+ if (ba[1].u > tba->b && ba[1].b < tba->u) {
+ lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
+ }
+ if (ba[2].u > tba->b && ba[2].b < tba->u) {
+ lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
+ }
+ }
+ }
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
+ for (lip2 = ((LineartBoundingArea *)lip->data)->lp.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ lineart_list_remove_pointer_item_no_free(&((LineartBoundingArea *)lip->data)->lp, lip2);
+ if (ba[0].u > tba->b && ba[0].b < tba->u) {
+ lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
+ }
+ if (ba[3].u > tba->b && ba[3].b < tba->u) {
+ lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
+ }
+ }
+ }
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &root->up) {
+ for (lip2 = ((LineartBoundingArea *)lip->data)->bp.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ lineart_list_remove_pointer_item_no_free(&((LineartBoundingArea *)lip->data)->bp, lip2);
+ if (ba[0].r > tba->l && ba[0].l < tba->r) {
+ lineart_list_append_pointer_pool(&tba->up, mph, &ba[0]);
+ }
+ if (ba[1].r > tba->l && ba[1].l < tba->r) {
+ lineart_list_append_pointer_pool(&tba->up, mph, &ba[1]);
+ }
+ }
+ }
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
+ for (lip2 = ((LineartBoundingArea *)lip->data)->up.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ lineart_list_remove_pointer_item_no_free(&((LineartBoundingArea *)lip->data)->up, lip2);
+ if (ba[2].r > tba->l && ba[2].l < tba->r) {
+ lineart_list_append_pointer_pool(&tba->bp, mph, &ba[2]);
+ }
+ if (ba[3].r > tba->l && ba[3].l < tba->r) {
+ lineart_list_append_pointer_pool(&tba->bp, mph, &ba[3]);
+ }
+ }
+ }
+ }
+
+ /* Finally clear parent'scene adjacent list. */
+ BLI_listbase_clear(&root->lp);
+ BLI_listbase_clear(&root->rp);
+ BLI_listbase_clear(&root->up);
+ BLI_listbase_clear(&root->bp);
+}
+
+static void lineart_bounding_area_split(LineartRenderBuffer *rb,
+ LineartBoundingArea *root,
+ int recursive_level)
+{
+ LineartBoundingArea *ba = lineart_mem_aquire(&rb->render_data_pool,
+ sizeof(LineartBoundingArea) * 4);
+ LineartRenderTriangle *rt;
+ LineartRenderLine *rl;
+
+ ba[0].l = root->cx;
+ ba[0].r = root->r;
+ ba[0].u = root->u;
+ ba[0].b = root->cy;
+ ba[0].cx = (ba[0].l + ba[0].r) / 2;
+ ba[0].cy = (ba[0].u + ba[0].b) / 2;
+
+ ba[1].l = root->l;
+ ba[1].r = root->cx;
+ ba[1].u = root->u;
+ ba[1].b = root->cy;
+ ba[1].cx = (ba[1].l + ba[1].r) / 2;
+ ba[1].cy = (ba[1].u + ba[1].b) / 2;
+
+ ba[2].l = root->l;
+ ba[2].r = root->cx;
+ ba[2].u = root->cy;
+ ba[2].b = root->b;
+ ba[2].cx = (ba[2].l + ba[2].r) / 2;
+ ba[2].cy = (ba[2].u + ba[2].b) / 2;
+
+ ba[3].l = root->cx;
+ ba[3].r = root->r;
+ ba[3].u = root->cy;
+ ba[3].b = root->b;
+ ba[3].cx = (ba[3].l + ba[3].r) / 2;
+ ba[3].cy = (ba[3].u + ba[3].b) / 2;
+
+ root->child = ba;
+
+ lineart_bounding_areas_connect_new(rb, root);
+
+ while ((rt = lineart_list_pop_pointer_no_free(&root->linked_triangles)) != NULL) {
+ LineartBoundingArea *cba = root->child;
+ double b[4];
+ b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ if (LRT_BOUND_AREA_CROSSES(b, &cba[0].l)) {
+ lineart_bounding_area_link_triangle(rb, &cba[0], rt, b, 0, recursive_level + 1, false);
+ }
+ if (LRT_BOUND_AREA_CROSSES(b, &cba[1].l)) {
+ lineart_bounding_area_link_triangle(rb, &cba[1], rt, b, 0, recursive_level + 1, false);
+ }
+ if (LRT_BOUND_AREA_CROSSES(b, &cba[2].l)) {
+ lineart_bounding_area_link_triangle(rb, &cba[2], rt, b, 0, recursive_level + 1, false);
+ }
+ if (LRT_BOUND_AREA_CROSSES(b, &cba[3].l)) {
+ lineart_bounding_area_link_triangle(rb, &cba[3], rt, b, 0, recursive_level + 1, false);
+ }
+ }
+
+ while ((rl = lineart_list_pop_pointer_no_free(&root->linked_lines)) != NULL) {
+ lineart_bounding_area_link_line(rb, root, rl);
+ }
+
+ rb->bounding_area_count += 3;
+}
+
+static int lineart_bounding_area_line_crossed(LineartRenderBuffer *UNUSED(fb),
+ double l[2],
+ double r[2],
+ LineartBoundingArea *ba)
+{
+ double vx, vy;
+ 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]))) {
+ return 0;
+ }
+
+ vx = l[0] - r[0];
+ vy = l[1] - r[1];
+
+ c1 = vx * (converted[2] - l[1]) - vy * (converted[0] - l[0]);
+ c = c1;
+
+ c1 = vx * (converted[2] - l[1]) - vy * (converted[1] - l[0]);
+ if (c1 * c <= 0) {
+ return 1;
+ }
+ else {
+ c = c1;
+ }
+
+ c1 = vx * (converted[3] - l[1]) - vy * (converted[0] - l[0]);
+ if (c1 * c <= 0) {
+ return 1;
+ }
+ else {
+ c = c1;
+ }
+
+ c1 = vx * (converted[3] - l[1]) - vy * (converted[1] - l[0]);
+ if (c1 * c <= 0) {
+ return 1;
+ }
+ else {
+ c = c1;
+ }
+
+ return 0;
+}
+
+static int lineart_bounding_area_triangle_covered(LineartRenderBuffer *fb,
+ LineartRenderTriangle *rt,
+ LineartBoundingArea *ba)
+{
+ double p1[2], p2[2], p3[2], p4[2];
+ double *FBC1 = rt->v[0]->fbcoord, *FBC2 = rt->v[1]->fbcoord, *FBC3 = rt->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;
+
+ 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])) {
+ return 1;
+ }
+
+ if (ED_lineart_point_inside_triangled(p1, FBC1, FBC2, FBC3) ||
+ ED_lineart_point_inside_triangled(p2, FBC1, FBC2, FBC3) ||
+ ED_lineart_point_inside_triangled(p3, FBC1, FBC2, FBC3) ||
+ ED_lineart_point_inside_triangled(p4, FBC1, FBC2, FBC3)) {
+ return 1;
+ }
+
+ if ((lineart_bounding_area_line_crossed(fb, FBC1, FBC2, ba)) ||
+ (lineart_bounding_area_line_crossed(fb, FBC2, FBC3, ba)) ||
+ (lineart_bounding_area_line_crossed(fb, FBC3, FBC1, ba))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
+ LineartBoundingArea *root_ba,
+ LineartRenderTriangle *rt,
+ double *LRUB,
+ int recursive,
+ int recursive_level,
+ bool do_intersection)
+{
+ if (!lineart_bounding_area_triangle_covered(rb, rt, root_ba)) {
+ return;
+ }
+ if (root_ba->child == NULL) {
+ lineart_list_append_pointer_pool(&root_ba->linked_triangles, &rb->render_data_pool, rt);
+ root_ba->triangle_count++;
+ /* If splitting doesn't improve triangle separation, then shouldn't allow splitting anymore.
+ * Here we use recursive limit. This is espetially useful in ortho render, where a lot of
+ * faces could easily line up perfectly in image space, which can not be separated by simply
+ * slicing the image tile. */
+ if (root_ba->triangle_count > 200 && recursive && recursive_level < 10) {
+ lineart_bounding_area_split(rb, root_ba, recursive_level);
+ }
+ if (recursive && do_intersection && rb->use_intersections) {
+ lineart_triangle_intersections_in_bounding_area(rb, rt, root_ba);
+ }
+ }
+ else {
+ LineartBoundingArea *ba = root_ba->child;
+ double *B1 = LRUB;
+ double b[4];
+ if (!LRUB) {
+ b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ B1 = b;
+ }
+ if (LRT_BOUND_AREA_CROSSES(B1, &ba[0].l)) {
+ lineart_bounding_area_link_triangle(
+ rb, &ba[0], rt, B1, recursive, recursive_level + 1, do_intersection);
+ }
+ if (LRT_BOUND_AREA_CROSSES(B1, &ba[1].l)) {
+ lineart_bounding_area_link_triangle(
+ rb, &ba[1], rt, B1, recursive, recursive_level + 1, do_intersection);
+ }
+ if (LRT_BOUND_AREA_CROSSES(B1, &ba[2].l)) {
+ lineart_bounding_area_link_triangle(
+ rb, &ba[2], rt, B1, recursive, recursive_level + 1, do_intersection);
+ }
+ if (LRT_BOUND_AREA_CROSSES(B1, &ba[3].l)) {
+ lineart_bounding_area_link_triangle(
+ rb, &ba[3], rt, B1, recursive, recursive_level + 1, do_intersection);
+ }
+ }
+}
+
+static void lineart_bounding_area_link_line(LineartRenderBuffer *rb,
+ LineartBoundingArea *root_ba,
+ LineartRenderLine *rl)
+{
+ if (root_ba->child == NULL) {
+ lineart_list_append_pointer_pool(&root_ba->linked_lines, &rb->render_data_pool, rl);
+ }
+ else {
+ if (lineart_bounding_area_line_crossed(
+ rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[0])) {
+ lineart_bounding_area_link_line(rb, &root_ba->child[0], rl);
+ }
+ if (lineart_bounding_area_line_crossed(
+ rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[1])) {
+ lineart_bounding_area_link_line(rb, &root_ba->child[1], rl);
+ }
+ if (lineart_bounding_area_line_crossed(
+ rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[2])) {
+ lineart_bounding_area_link_line(rb, &root_ba->child[2], rl);
+ }
+ if (lineart_bounding_area_line_crossed(
+ rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[3])) {
+ lineart_bounding_area_link_line(rb, &root_ba->child[3], rl);
+ }
+ }
+}
+
+static int lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb,
+ LineartRenderTriangle *rt,
+ int *rowbegin,
+ int *rowend,
+ int *colbegin,
+ int *colend)
+{
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ double b[4];
+
+ if (!rt->v[0] || !rt->v[1] || !rt->v[2]) {
+ return 0;
+ }
+
+ b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[2] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[3] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+
+ if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
+ return 0;
+ }
+
+ (*colbegin) = (int)((b[0] + 1.0) / sp_w);
+ (*colend) = (int)((b[1] + 1.0) / sp_w);
+ (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1;
+ (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1;
+
+ if ((*colend) >= rb->tile_count_x) {
+ (*colend) = rb->tile_count_x - 1;
+ }
+ if ((*rowend) >= rb->tile_count_y) {
+ (*rowend) = rb->tile_count_y - 1;
+ }
+ if ((*colbegin) < 0) {
+ (*colbegin) = 0;
+ }
+ if ((*rowbegin) < 0) {
+ (*rowbegin) = 0;
+ }
+
+ return 1;
+}
+
+static int lineart_get_line_bounding_areas(LineartRenderBuffer *rb,
+ LineartRenderLine *rl,
+ int *rowbegin,
+ int *rowend,
+ int *colbegin,
+ int *colend)
+{
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ double b[4];
+
+ if (!rl->l || !rl->r) {
+ return 0;
+ }
+
+ if (rl->l->fbcoord[0] != rl->l->fbcoord[0] || rl->r->fbcoord[0] != rl->r->fbcoord[0]) {
+ return 0;
+ }
+
+ b[0] = MIN2(rl->l->fbcoord[0], rl->r->fbcoord[0]);
+ b[1] = MAX2(rl->l->fbcoord[0], rl->r->fbcoord[0]);
+ b[2] = MIN2(rl->l->fbcoord[1], rl->r->fbcoord[1]);
+ b[3] = MAX2(rl->l->fbcoord[1], rl->r->fbcoord[1]);
+
+ if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
+ return 0;
+ }
+
+ (*colbegin) = (int)((b[0] + 1.0) / sp_w);
+ (*colend) = (int)((b[1] + 1.0) / sp_w);
+ (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1;
+ (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1;
+
+ /* It'scene possible that the line stretches too much out to the side, resulting negative value
+ . */
+ if ((*rowend) < (*rowbegin)) {
+ (*rowend) = rb->tile_count_y - 1;
+ }
+
+ if ((*colend) < (*colbegin)) {
+ (*colend) = rb->tile_count_x - 1;
+ }
+
+ CLAMP((*colbegin), 0, rb->tile_count_x - 1);
+ CLAMP((*rowbegin), 0, rb->tile_count_y - 1);
+ CLAMP((*colend), 0, rb->tile_count_x - 1);
+ CLAMP((*rowend), 0, rb->tile_count_y - 1);
+
+ return 1;
+}
+
+LineartBoundingArea *ED_lineart_get_point_bounding_area(LineartRenderBuffer *rb,
+ double x,
+ double y)
+{
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ int col, row;
+
+ if (x > 1 || x < -1 || y > 1 || y < -1) {
+ return 0;
+ }
+
+ col = (int)((x + 1.0) / sp_w);
+ row = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1;
+
+ if (col >= rb->tile_count_x) {
+ col = rb->tile_count_x - 1;
+ }
+ if (row >= rb->tile_count_y) {
+ row = rb->tile_count_y - 1;
+ }
+ if (col < 0) {
+ col = 0;
+ }
+ if (row < 0) {
+ row = 0;
+ }
+
+ return &rb->initial_bounding_areas[row * 4 + col];
+}
+
+static LineartBoundingArea *lineart_get_point_bounding_area_recursive(LineartBoundingArea *ba,
+ double x,
+ double y)
+{
+ if (ba->child == NULL) {
+ return ba;
+ }
+ else {
+#define IN_BOUND(i, x, y) \
+ ba->child[i].l <= x && ba->child[i].r >= x && ba->child[i].b <= y && ba->child[i].u >= y
+
+ if (IN_BOUND(0, x, y)) {
+ return lineart_get_point_bounding_area_recursive(&ba->child[0], x, y);
+ }
+ else if (IN_BOUND(1, x, y)) {
+ return lineart_get_point_bounding_area_recursive(&ba->child[1], x, y);
+ }
+ else if (IN_BOUND(2, x, y)) {
+ return lineart_get_point_bounding_area_recursive(&ba->child[2], x, y);
+ }
+ else if (IN_BOUND(3, x, y)) {
+ return lineart_get_point_bounding_area_recursive(&ba->child[3], x, y);
+ }
+ }
+ return NULL;
+#undef IN_BOUND
+}
+
+LineartBoundingArea *ED_lineart_get_point_bounding_area_deep(LineartRenderBuffer *rb,
+ double x,
+ double y)
+{
+ LineartBoundingArea *ba;
+ if ((ba = ED_lineart_get_point_bounding_area(rb, x, y)) != NULL) {
+ return lineart_get_point_bounding_area_recursive(ba, x, y);
+ }
+ return NULL;
+}
+
+static void lineart_main_add_triangles(LineartRenderBuffer *rb)
+{
+ LineartRenderTriangle *rt;
+ int i, lim;
+ int x1, x2, y1, y2;
+ int r, co;
+ int temp_count = 0;
+
+ LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
+ rt = reln->pointer;
+ lim = reln->element_count;
+ for (i = 0; i < lim; i++) {
+ if ((rt->flags & LRT_CULL_USED) || (rt->flags & LRT_CULL_DISCARD)) {
+ rt = (void *)(((unsigned char *)rt) + rb->triangle_size);
+ continue;
+ }
+ if (lineart_get_triangle_bounding_areas(rb, rt, &y1, &y2, &x1, &x2)) {
+ for (co = x1; co <= x2; co++) {
+ for (r = y1; r <= y2; r++) {
+ lineart_bounding_area_link_triangle(rb,
+ &rb->initial_bounding_areas[r * 4 + co],
+ rt,
+ 0,
+ 1,
+ 0,
+ (!(rt->flags & LRT_TRIANGLE_NO_INTERSECTION)));
+ }
+ }
+ temp_count++;
+ } /* else throw away. */
+ rt = (void *)(((unsigned char *)rt) + rb->triangle_size);
+ if ((!(temp_count % 1000)) && ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ return;
+ }
+ }
+ }
+}
+
+/** 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,
+ LineartRenderLine *rl,
+ 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;
+ LineartBoundingArea *ba;
+
+ /* If we are marching towards the right. */
+ if (positive_x > 0) {
+ rx = this->r;
+ ry = y + k * (rx - x);
+
+ /* If we are marching towards the top. */
+ if (positive_y > 0) {
+ uy = this->u;
+ ux = x + (uy - y) / k;
+ r1 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], rx);
+ r2 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], ux);
+ if (MIN2(r1, r2) > 1) {
+ return 0;
+ }
+
+ /* we reached the right side before the top side. */
+ if (r1 <= r2) {
+ LISTBASE_FOREACH (LinkData *, lip, &this->rp) {
+ ba = lip->data;
+ if (ba->u >= ry && ba->b < ry) {
+ *next_x = rx;
+ *next_y = ry;
+ return ba;
+ }
+ }
+ }
+ /* we reached the top side before the right side. */
+ else {
+ LISTBASE_FOREACH (LinkData *, lip, &this->up) {
+ ba = lip->data;
+ if (ba->r >= ux && ba->l < ux) {
+ *next_x = ux;
+ *next_y = uy;
+ return ba;
+ }
+ }
+ }
+ }
+ /* If we are marching towards the bottom. */
+ else if (positive_y < 0) {
+ by = this->b;
+ bx = x + (by - y) / k;
+ r1 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], rx);
+ r2 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], bx);
+ if (MIN2(r1, r2) > 1) {
+ return 0;
+ }
+ if (r1 <= r2) {
+ LISTBASE_FOREACH (LinkData *, lip, &this->rp) {
+ ba = lip->data;
+ if (ba->u >= ry && ba->b < ry) {
+ *next_x = rx;
+ *next_y = ry;
+ return ba;
+ }
+ }
+ }
+ else {
+ LISTBASE_FOREACH (LinkData *, lip, &this->bp) {
+ ba = lip->data;
+ if (ba->r >= bx && ba->l < bx) {
+ *next_x = bx;
+ *next_y = by;
+ return ba;
+ }
+ }
+ }
+ }
+ /* If the line is compeletely horizontal, in which Y diffence == 0. */
+ else {
+ r1 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], this->r);
+ if (r1 > 1) {
+ return 0;
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &this->rp) {
+ ba = lip->data;
+ if (ba->u >= y && ba->b < y) {
+ *next_x = this->r;
+ *next_y = y;
+ return ba;
+ }
+ }
+ }
+ }
+
+ /* If we are marching towards the left. */
+ else if (positive_x < 0) {
+ lx = this->l;
+ ly = y + k * (lx - x);
+
+ /* If we are marching towards the top. */
+ if (positive_y > 0) {
+ uy = this->u;
+ ux = x + (uy - y) / k;
+ r1 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], lx);
+ r2 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], ux);
+ if (MIN2(r1, r2) > 1) {
+ return 0;
+ }
+ if (r1 <= r2) {
+ LISTBASE_FOREACH (LinkData *, lip, &this->lp) {
+ ba = lip->data;
+ if (ba->u >= ly && ba->b < ly) {
+ *next_x = lx;
+ *next_y = ly;
+ return ba;
+ }
+ }
+ }
+ else {
+ LISTBASE_FOREACH (LinkData *, lip, &this->up) {
+ ba = lip->data;
+ if (ba->r >= ux && ba->l < ux) {
+ *next_x = ux;
+ *next_y = uy;
+ return ba;
+ }
+ }
+ }
+ }
+
+ /* If we are marching towards the bottom. */
+ else if (positive_y < 0) {
+ by = this->b;
+ bx = x + (by - y) / k;
+ r1 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], lx);
+ r2 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], bx);
+ if (MIN2(r1, r2) > 1) {
+ return 0;
+ }
+ if (r1 <= r2) {
+ LISTBASE_FOREACH (LinkData *, lip, &this->lp) {
+ ba = lip->data;
+ if (ba->u >= ly && ba->b < ly) {
+ *next_x = lx;
+ *next_y = ly;
+ return ba;
+ }
+ }
+ }
+ else {
+ LISTBASE_FOREACH (LinkData *, lip, &this->bp) {
+ ba = lip->data;
+ if (ba->r >= bx && ba->l < bx) {
+ *next_x = bx;
+ *next_y = by;
+ return ba;
+ }
+ }
+ }
+ }
+ /* Again, horizontal. */
+ else {
+ r1 = ratiod(rl->l->fbcoord[0], rl->r->fbcoord[0], this->l);
+ if (r1 > 1) {
+ return 0;
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &this->lp) {
+ ba = lip->data;
+ if (ba->u >= y && ba->b < y) {
+ *next_x = this->l;
+ *next_y = y;
+ return ba;
+ }
+ }
+ }
+ }
+ /* If the line is completely vertical, hence X difference == 0. */
+ else {
+ if (positive_y > 0) {
+ r1 = ratiod(rl->l->fbcoord[1], rl->r->fbcoord[1], this->u);
+ if (r1 > 1) {
+ return 0;
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &this->up) {
+ ba = lip->data;
+ if (ba->r > x && ba->l <= x) {
+ *next_x = x;
+ *next_y = this->u;
+ return ba;
+ }
+ }
+ }
+ else if (positive_y < 0) {
+ r1 = ratiod(rl->l->fbcoord[1], rl->r->fbcoord[1], this->b);
+ if (r1 > 1) {
+ return 0;
+ }
+ LISTBASE_FOREACH (LinkData *, lip, &this->bp) {
+ ba = lip->data;
+ if (ba->r > x && ba->l <= x) {
+ *next_x = x;
+ *next_y = this->b;
+ return ba;
+ }
+ }
+ }
+ else
+ return 0; /* segment has no length. */
+ }
+ return 0;
+}
+
+static LineartBoundingArea *lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y)
+{
+ LineartBoundingArea *iba;
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ int c = (int)((x + 1.0) / sp_w);
+ int r = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1;
+ if (r < 0) {
+ r = 0;
+ }
+ if (c < 0) {
+ c = 0;
+ }
+ if (r >= rb->tile_count_y) {
+ r = rb->tile_count_y - 1;
+ }
+ if (c >= rb->tile_count_x) {
+ c = rb->tile_count_x - 1;
+ }
+
+ iba = &rb->initial_bounding_areas[r * 4 + c];
+ while (iba->child) {
+ if (x > iba->cx) {
+ if (y > iba->cy) {
+ iba = &iba->child[0];
+ }
+ else {
+ iba = &iba->child[3];
+ }
+ }
+ else {
+ if (y > iba->cy) {
+ iba = &iba->child[1];
+ }
+ else {
+ iba = &iba->child[2];
+ }
+ }
+ }
+ return iba;
+}
+
+static LineartBoundingArea *linear_bounding_areat_first_possible(LineartRenderBuffer *rb,
+ LineartRenderLine *rl)
+{
+ LineartBoundingArea *iba;
+ double data[2] = {rl->l->fbcoord[0], rl->l->fbcoord[1]};
+ double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1};
+ double r = 1, sr = 1;
+
+ if (data[0] > -1 && data[0] < 1 && data[1] > -1 && data[1] < 1) {
+ return lineart_get_bounding_area(rb, data[0], data[1]);
+ }
+ else {
+ if (lineart_LineIntersectTest2d(rl->l->fbcoord, rl->r->fbcoord, LU, RU, &sr) && sr < r &&
+ sr > 0) {
+ r = sr;
+ }
+ if (lineart_LineIntersectTest2d(rl->l->fbcoord, rl->r->fbcoord, LB, RB, &sr) && sr < r &&
+ sr > 0) {
+ r = sr;
+ }
+ if (lineart_LineIntersectTest2d(rl->l->fbcoord, rl->r->fbcoord, LB, LU, &sr) && sr < r &&
+ sr > 0) {
+ r = sr;
+ }
+ if (lineart_LineIntersectTest2d(rl->l->fbcoord, rl->r->fbcoord, RB, RU, &sr) && sr < r &&
+ sr > 0) {
+ r = sr;
+ }
+ interp_v2_v2v2_db(data, rl->l->fbcoord, rl->r->fbcoord, r);
+
+ return lineart_get_bounding_area(rb, data[0], data[1]);
+ }
+
+ return iba;
+}
+
+/* Calculations. */
+
+/** Parent thread locking should be done before this very function is called. */
+int ED_lineart_compute_feature_lines_internal(Depsgraph *depsgraph, const int show_frame_progress)
+{
+ LineartRenderBuffer *rb;
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ SceneLineArt *lineart = &scene->lineart;
+ int intersections_only = 0; /* Not used right now, but preserve for future. */
+
+ if ((lineart->flags & LRT_AUTO_UPDATE) == 0 || !scene->camera) {
+ /* Release lock when early return. */
+ BLI_spin_unlock(&lineart_share.lock_loader);
+ return OPERATOR_CANCELLED;
+ }
+
+#define LRT_PROGRESS(progress, message) \
+ if (show_frame_progress) { \
+ ED_lineart_update_render_progress(progress, message); \
+ }
+
+#define LRT_CANCEL_STAGE \
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) { \
+ LRT_PROGRESS(100, "LRT: Finished."); \
+ return OPERATOR_FINISHED; \
+ }
+
+ rb = ED_lineart_create_render_buffer(scene);
+
+ /* Has to be set after render buffer creation, to avoid locking from editor undo. */
+ ED_lineart_calculation_flag_set(LRT_RENDER_RUNNING);
+
+ lineart_share.render_buffer_shared = rb;
+
+ rb->triangle_size = lineart_triangle_size_get(scene);
+
+ rb->max_occlusion_level = lineart_occlusion_get_max_level(depsgraph);
+
+ LRT_PROGRESS(0, "LRT: Loading geometries.");
+
+ lineart_main_get_view_vector(rb);
+ lineart_main_load_geometries(depsgraph, scene, scene->camera, rb);
+
+ /** We had everything we need,
+ * Unlock parent thread, it'scene safe to run independently from now. */
+ BLI_spin_unlock(&lineart_share.lock_loader);
+
+ if (!rb->vertex_buffer_pointers.first) {
+ /* Nothing loaded, early return. */
+ LRT_PROGRESS(100, "LRT: Finished.");
+ return OPERATOR_FINISHED;
+ }
+
+ LRT_CANCEL_STAGE
+ LRT_PROGRESS(10, "LRT: Culling.");
+
+ lineart_main_bounding_area_make_initial(rb);
+
+ lineart_main_cull_triangles(rb, false);
+ lineart_main_cull_triangles(rb, true);
+
+ lineart_main_free_adjacent_data(rb);
+
+ lineart_main_perspective_division(rb);
+
+ LRT_CANCEL_STAGE
+ LRT_PROGRESS(25, "LRT: Intersections.");
+
+ lineart_main_add_triangles(rb);
+
+ LRT_CANCEL_STAGE
+ LRT_PROGRESS(50, "LRT: Occlusion.");
+
+ if (!intersections_only) {
+ lineart_main_occlusion_begin(rb);
+ }
+
+ LRT_CANCEL_STAGE
+ LRT_PROGRESS(75, "LRT: Chaining.");
+
+ /* intersection_only is preserved for furure functions.*/
+ if (!intersections_only) {
+ float t_image = scene->lineart.chaining_image_threshold;
+ float t_geom = scene->lineart.chaining_geometry_threshold;
+
+ ED_lineart_chain_feature_lines(rb);
+
+ ED_lineart_chain_split_for_fixed_occlusion(rb);
+
+ if (t_image < FLT_EPSILON && t_geom < FLT_EPSILON) {
+ t_geom = 0.0f;
+ t_image = 0.01f;
+ }
+
+ ED_lineart_chain_connect(rb, 1);
+ ED_lineart_chain_clear_picked_flag(rb);
+ ED_lineart_chain_connect(rb, 0);
+
+ /* This configuration ensures there won't be accidental lost of short segments. */
+ ED_lineart_chain_discard_short(rb, MIN3(t_image, t_geom, 0.01f) - FLT_EPSILON);
+
+ if (rb->angle_splitting_threshold > 0.0001) {
+ ED_lineart_chain_split_angle(rb, rb->angle_splitting_threshold);
+ }
+ }
+
+ LRT_PROGRESS(100, "LRT: Finished.");
+
+#undef LRT_PROGRESS
+#undef LRT_CANCEL_STAGE
+ if (G.debug_value == 4000) {
+ lineart_count_and_print_render_buffer_memory(rb);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+typedef struct LRT_FeatureLineWorker {
+ Depsgraph *dg;
+ int intersection_only;
+ int show_frame_progress;
+} LRT_FeatureLineWorker;
+
+static void lineart_gpencil_notify_targets(Depsgraph *dg);
+
+static void lineart_compute_feature_lines_worker(TaskPool *__restrict UNUSED(pool),
+ LRT_FeatureLineWorker *worker_data)
+{
+
+ ED_lineart_compute_feature_lines_internal(worker_data->dg, worker_data->show_frame_progress);
+
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_CANCELING)) {
+ ED_lineart_calculation_flag_set(LRT_RENDER_FINISHED);
+ return;
+ }
+
+ ED_lineart_chain_clear_picked_flag(lineart_share.render_buffer_shared);
+
+ /* Calculation is done, give fresh data. */
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_FRESH, false);
+
+ lineart_gpencil_notify_targets(worker_data->dg);
+
+ ED_lineart_calculation_flag_set(LRT_RENDER_FINISHED);
+}
+
+void ED_lineart_compute_feature_lines_background(Depsgraph *dg, const int show_frame_progress)
+{
+ TaskPool *tp_read;
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ tp_read = lineart_share.pending_render_task;
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+
+ /* If the calculation is already started then bypass it. */
+ if (ED_lineart_calculation_flag_check(LRT_RENDER_RUNNING)) {
+ /* Set CANCEL flag, and when operation is canceled, flag will become FINISHED. */
+ ED_lineart_calculation_flag_set(LRT_RENDER_CANCELING);
+ if (G.debug_value == 4000) {
+ printf("LRT: Canceling.\n");
+ }
+ /* Can't release the lock just right now, because loading function might still be canceling
+ . */
+ }
+
+ if (tp_read) {
+ BLI_spin_unlock(&lineart_share.lock_loader);
+ return;
+ }
+
+ LRT_FeatureLineWorker *flw = MEM_callocN(sizeof(LRT_FeatureLineWorker), "Line Art Worker");
+
+ flw->dg = dg;
+ flw->intersection_only = 0 /* Not used for CPU. */;
+ flw->show_frame_progress = show_frame_progress;
+
+ TaskPool *tp = BLI_task_pool_create_background(0, TASK_PRIORITY_HIGH);
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ lineart_share.pending_render_task = tp;
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+
+ BLI_task_pool_push(tp, (TaskRunFunction)lineart_compute_feature_lines_worker, flw, true, NULL);
+}
+
+/* Grease Pencil bindings. */
+
+static void lineart_gpencil_notify_targets(Depsgraph *dg)
+{
+ DEG_OBJECT_ITER_BEGIN (dg,
+ ob,
+ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) {
+ if (ob->type == OB_GPENCIL) {
+ if (BKE_gpencil_modifiers_findby_type(ob, eGpencilModifierType_Lineart)) {
+ bGPdata *gpd = ((Object *)ob->id.orig_id)->data;
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ }
+ }
+ }
+ DEG_OBJECT_ITER_END;
+}
+
+static int lineart_rb_line_types(LineartRenderBuffer *rb)
+{
+ int types = 0;
+ types |= rb->use_contour ? LRT_EDGE_FLAG_CONTOUR : 0;
+ types |= rb->use_crease ? LRT_EDGE_FLAG_CREASE : 0;
+ types |= rb->use_material ? LRT_EDGE_FLAG_MATERIAL : 0;
+ types |= rb->use_edge_marks ? LRT_EDGE_FLAG_EDGE_MARK : 0;
+ types |= rb->use_intersections ? LRT_EDGE_FLAG_INTERSECTION : 0;
+ return types;
+}
+
+void ED_lineart_gpencil_generate(Depsgraph *depsgraph,
+ Object *gpencil_object,
+ float (*gp_obmat_inverse)[4],
+ bGPDlayer *UNUSED(gpl),
+ bGPDframe *gpf,
+ int level_start,
+ int level_end,
+ int material_nr,
+ Object *source_object,
+ Collection *source_collection,
+ int types,
+ unsigned char transparency_flags,
+ unsigned char transparency_mask,
+ short thickness,
+ float opacity,
+ float pre_sample_length,
+ const char *source_vgname,
+ const char *vgname,
+ int modifier_flags)
+{
+ LineartRenderBuffer *rb = lineart_share.render_buffer_shared;
+
+ if (rb == NULL) {
+ if (G.debug_value == 4000) {
+ printf("NULL Lineart rb!\n");
+ }
+ return;
+ }
+
+ if ((!lineart_share.init_complete) || !ED_lineart_calculation_flag_check(LRT_RENDER_FINISHED)) {
+ /* cache not ready. */
+ if (G.debug_value == 4000) {
+ printf("Line art cache isn't ready!\n");
+ }
+ return;
+ }
+ else {
+ /* lock the cache, prevent rendering job from starting. */
+ BLI_spin_lock(&lineart_share.lock_render_status);
+ }
+ int stroke_count = 0;
+ int color_idx = 0;
+
+ Object *orig_ob = NULL;
+ if (source_object) {
+ orig_ob = source_object->id.orig_id ? (Object *)source_object->id.orig_id : source_object;
+ }
+
+ Collection *orig_col = NULL;
+ if (source_collection) {
+ orig_col = source_collection->id.orig_id ? (Collection *)source_collection->id.orig_id :
+ source_collection;
+ }
+
+ /* if(!orig_col && !orig_ob) means the whole scene is selected. */
+
+ float mat[4][4];
+ unit_m4(mat);
+
+ int enabled_types = lineart_rb_line_types(rb);
+ bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP;
+ bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP;
+ bool preserve_weight = modifier_flags & LRT_GPENCIL_SOFT_SELECTION;
+
+ LISTBASE_FOREACH (LineartRenderLineChain *, rlc, &rb->chains) {
+
+ if (rlc->picked) {
+ continue;
+ }
+ if (!(rlc->type & (types & enabled_types))) {
+ continue;
+ }
+ if (rlc->level > level_end || rlc->level < level_start) {
+ continue;
+ }
+ if (orig_ob && orig_ob != rlc->object_ref) {
+ continue;
+ }
+ if (orig_col && rlc->object_ref) {
+ if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)rlc->object_ref)) {
+ continue;
+ }
+ }
+ if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_ENABLE) {
+ if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_MATCH) {
+ if (rlc->transparency_mask != transparency_mask) {
+ continue;
+ }
+ }
+ else {
+ if (!(rlc->transparency_mask & transparency_mask)) {
+ continue;
+ }
+ }
+ }
+
+ /* Modifier for different GP objects are not evaluated in order, thus picked flag doesn't
+ * quite make sense. Should have a batter solution if we don't want to pick the same stroke
+ * twice. */
+ /* rlc->picked = 1;. */
+
+ int array_idx = 0;
+ int count = ED_lineart_chain_count(rlc);
+ bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false);
+
+ float *stroke_data = MEM_callocN(sizeof(float) * count * GP_PRIM_DATABUF_SIZE,
+ "line art add stroke");
+
+ LISTBASE_FOREACH (LineartRenderLineChainItem *, rlci, &rlc->chain) {
+ stroke_data[array_idx] = rlci->gpos[0];
+ stroke_data[array_idx + 1] = rlci->gpos[1];
+ stroke_data[array_idx + 2] = rlci->gpos[2];
+ mul_m4_v3(gp_obmat_inverse, &stroke_data[array_idx]);
+ stroke_data[array_idx + 3] = 1; /* thickness. */
+ stroke_data[array_idx + 4] = opacity; /* hardness?. */
+ array_idx += 5;
+ }
+
+ BKE_gpencil_stroke_add_points(gps, stroke_data, count, mat);
+ BKE_gpencil_dvert_ensure(gps);
+ gps->mat_nr = material_nr;
+
+ MEM_freeN(stroke_data);
+
+ if (source_vgname && vgname) {
+ Object *eval_ob = DEG_get_evaluated_object(depsgraph, rlc->object_ref);
+ int gpdg = -1;
+ if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) {
+ if (eval_ob && eval_ob->type == OB_MESH) {
+ int dindex = 0;
+ Mesh *me = (Mesh *)eval_ob->data;
+ if (me->dvert) {
+ LISTBASE_FOREACH (bDeformGroup *, db, &eval_ob->defbase) {
+ if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) {
+ if (match_output) {
+ gpdg = BKE_object_defgroup_name_index(gpencil_object, db->name);
+ if (gpdg < 0) {
+ continue;
+ }
+ }
+ int sindex = 0, vindex;
+ LISTBASE_FOREACH (LineartRenderLineChainItem *, rlci, &rlc->chain) {
+ vindex = rlci->index;
+ /* XXX: Here doesn't have post-modifier dvert!. */
+ if (vindex >= me->totvert) {
+ break;
+ }
+ MDeformWeight *mdw = BKE_defvert_ensure_index(&me->dvert[vindex], dindex);
+ MDeformWeight *gdw = BKE_defvert_ensure_index(&gps->dvert[sindex], gpdg);
+ if (preserve_weight) {
+ float use_weight = mdw->weight;
+ if (invert_input) {
+ use_weight = 1 - use_weight;
+ }
+ gdw->weight = MAX2(use_weight, gdw->weight);
+ }
+ else {
+ if (mdw->weight > 0.999f) {
+ gdw->weight = 1.0f;
+ }
+ }
+ sindex++;
+ }
+ }
+ dindex++;
+ }
+ }
+ }
+ }
+ }
+
+ if (pre_sample_length > 0.0001) {
+ BKE_gpencil_stroke_sample(gpencil_object->data, gps, pre_sample_length, false);
+ }
+ if (G.debug_value == 4000) {
+ BKE_gpencil_stroke_set_random_color(gps);
+ }
+ BKE_gpencil_stroke_geometry_update(gpencil_object->data, gps);
+ stroke_count++;
+ }
+
+ if (G.debug_value == 4000) {
+ printf("LRT: Generated %d strokes.\n", stroke_count);
+ }
+ /* release render lock so cache is free to be manipulated. */
+ BLI_spin_unlock(&lineart_share.lock_render_status);
+}
+
+void ED_lineart_gpencil_generate_with_type(Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ char source_type,
+ void *source_reference,
+ int level_start,
+ int level_end,
+ int mat_nr,
+ short line_types,
+ unsigned char transparency_flags,
+ unsigned char transparency_mask,
+ short thickness,
+ float opacity,
+ float pre_sample_length,
+ const char *source_vgname,
+ const char *vgname,
+ int modifier_flags)
+{
+
+ if (!gpl || !gpf || !ob) {
+ return;
+ }
+
+ Object *source_object = NULL;
+ Collection *source_collection = NULL;
+ short use_types = 0;
+ 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 = line_types & (~LRT_EDGE_FLAG_INTERSECTION);
+ }
+ else if (source_type == LRT_SOURCE_COLLECTION) {
+ if (!source_reference) {
+ return;
+ }
+ source_collection = (Collection *)source_reference;
+ use_types = line_types;
+ }
+ else {
+ /* Whole scene. */
+ use_types = line_types;
+ }
+ float gp_obmat_inverse[4][4];
+ invert_m4_m4(gp_obmat_inverse, ob->obmat);
+ ED_lineart_gpencil_generate(depsgraph,
+ ob,
+ gp_obmat_inverse,
+ gpl,
+ gpf,
+ level_start,
+ level_end,
+ mat_nr,
+ source_object,
+ source_collection,
+ use_types,
+ transparency_flags,
+ transparency_mask,
+ thickness,
+ opacity,
+ pre_sample_length,
+ source_vgname,
+ vgname,
+ modifier_flags);
+}
+
+void ED_lineart_post_frame_update_external(bContext *C,
+ Scene *scene,
+ Depsgraph *dg,
+ bool from_modifier)
+{
+ if (G.debug_value == 4000) {
+ printf("LRT: ---- Post frame update (%d).\n", 0);
+ }
+ if (!scene->camera) {
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_IDLE, false);
+ }
+ if (!(scene->lineart.flags & LRT_AUTO_UPDATE)) {
+ /* This way the modifier will update, removing remaing strokes in the viewport. */
+ if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_WAITING)) {
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_IDLE, false);
+ lineart_gpencil_notify_targets(dg);
+ }
+ return;
+ }
+ if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_WAITING)) {
+ /* Modifier is waiting for data, trigger update (will wait/cancel if already running). */
+ if (scene->lineart.flags & LRT_AUTO_UPDATE) {
+ if (C) {
+ lineart_share.wm = CTX_wm_manager(C);
+ lineart_share.main_window = lineart_share.wm->windows.first;
+ }
+ else {
+ lineart_share.wm = NULL;
+ lineart_share.main_window = NULL;
+ }
+
+ if (G.debug_value == 4000) {
+ printf("LRT: ---- Post frame update called at LRT_SYNC_WAITING, %s.\n",
+ from_modifier ? "from modifier" : "from scene update");
+ }
+
+ /** Lock caller thread before calling feature line computation.
+ * This worker is not a background task, so we don't need to try another lock
+ * to wait for the worker to finish. The lock will be released in the compute function.
+ . */
+ BLI_spin_lock(&lineart_share.lock_loader);
+ ED_lineart_compute_feature_lines_background(dg, 1);
+
+ /* Wait for loading finish. */
+ BLI_spin_lock(&lineart_share.lock_loader);
+ BLI_spin_unlock(&lineart_share.lock_loader);
+ }
+ }
+ else if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_FRESH)) {
+ bool is_render = (DEG_get_mode(dg) == DAG_EVAL_RENDER);
+
+ /* No double caching during rendering. */
+ if (ED_lineart_modifier_sync_still_has_customer()) {
+ return;
+ }
+
+ /* This code path is not working with motion blur on "render animation". not sure why, but
+ * here if we retain the data and restore the flag, results will be correct. (The wrong
+ * clearing happens when dg->mode == DAG_EVAL_VIEWPORT) so can't really catch it there.). */
+ if (is_render && (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED)) {
+ return;
+ }
+
+ /* To avoid double clearing. */
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_CLEARING, from_modifier);
+
+ /* Due to using GPencil modifiers, and the scene is updated each time some value is changed,
+ * we really don't need to keep the buffer any longer. If in the future we want fast refresh
+ * on parameter changes (e.g. thickness or picking different result in an already validated
+ * buffer), remove ED_lineart_destroy_render_data_external() below.*/
+ if (!from_modifier) {
+ if (G.debug_value == 4000) {
+ printf("LRT: ---- Destroy on update (%d).\n", is_render);
+ }
+
+ /* Currently the data is destroyed upon turning off line art. Destroying here post many
+ * problems as this function might get called during rendering for the viewport. */
+ /* ED_lineart_destroy_render_data_external();. */
+ }
+
+ /* At this stage GP should have all the data. We clear the flag. This is needed for real-time
+ * update on editing in the viewport. */
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_IDLE, from_modifier);
+ }
+}
+
+void ED_lineart_update_render_progress(int nr, const char *info)
+{
+ /* WM_cursor_set() and WM_cursor_time() should not be called in a background thread, need an
+ * alternative way of showing the progress. */
+ if (lineart_share.main_window) {
+ if (nr == 100) {
+ /* just setting WM_CURSOR_DEFAULT doesn't seem to work on linux. */
+ /* WM_cursor_set(lineart_share.main_window, WM_CURSOR_NW_ARROW);. */
+ }
+ else {
+ /* WM_cursor_time(lineart_share.main_window, nr);. */
+ WM_progress_set(lineart_share.main_window, (float)nr / 100);
+ }
+ }
+
+ if (G.debug_value == 4000) {
+ if (info) {
+ printf("%s\n", info);
+ }
+ }
+}
diff --git a/source/blender/editors/lineart/lineart_intern.h b/source/blender/editors/lineart/lineart_intern.h
new file mode 100644
index 00000000000..dc9c51bba84
--- /dev/null
+++ b/source/blender/editors/lineart/lineart_intern.h
@@ -0,0 +1,108 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#ifndef __LRT_INTERN_H__
+#define __LRT_INTERN_H__
+
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_threads.h"
+
+#include "DNA_lineart_types.h"
+
+#include <math.h>
+#include <string.h>
+
+struct LineartStaticMemPoolNode;
+struct LineartRenderLine;
+struct LineartRenderBuffer;
+
+void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *p);
+void *lineart_list_append_pointer_pool_sized(ListBase *h,
+ struct LineartStaticMemPool *smp,
+ void *p,
+ int size);
+void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p);
+void *list_push_pointer_static_sized(ListBase *h,
+ struct LineartStaticMemPool *smp,
+ void *p,
+ int size);
+
+void *lineart_list_pop_pointer_no_free(ListBase *h);
+void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
+
+LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp,
+ size_t size);
+void *lineart_mem_aquire(struct LineartStaticMemPool *smp, size_t size);
+void *lineart_mem_aquire_thread(struct LineartStaticMemPool *smp, size_t size);
+void lineart_mem_destroy(LineartStaticMemPool *smp);
+
+void lineart_prepend_line_direct(LineartRenderLine **first, void *node);
+void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link);
+
+void lineart_matrix_ortho_44d(double (*mProjection)[4],
+ double xMin,
+ double xMax,
+ double yMin,
+ double yMax,
+ double zMin,
+ double zMax);
+void lineart_matrix_perspective_44d(
+ double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax);
+
+int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb);
+
+void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb);
+
+#define LRT_ITER_ALL_LINES_BEGIN \
+ LineartRenderLine *rl, *next_rl, **current_list; \
+ rl = rb->contours; \
+ for (current_list = &rb->contours; rl; rl = next_rl) { \
+ next_rl = rl->next;
+
+#define LRT_ITER_ALL_LINES_END \
+ while (!next_rl) { \
+ if (current_list == &rb->contours) { \
+ current_list = &rb->crease_lines; \
+ } \
+ else if (current_list == &rb->crease_lines) { \
+ current_list = &rb->material_lines; \
+ } \
+ else if (current_list == &rb->material_lines) { \
+ current_list = &rb->edge_marks; \
+ } \
+ else if (current_list == &rb->edge_marks) { \
+ current_list = &rb->intersection_lines; \
+ } \
+ else { \
+ break; \
+ } \
+ next_rl = *current_list; \
+ } \
+ }
+
+#define LRT_BOUND_AREA_CROSSES(b1, b2) \
+ ((b1)[0] < (b2)[1] && (b1)[1] > (b2)[0] && (b1)[3] < (b2)[2] && (b1)[2] > (b2)[3])
+
+#endif
diff --git a/source/blender/editors/lineart/lineart_ops.c b/source/blender/editors/lineart/lineart_ops.c
new file mode 100644
index 00000000000..043c4b0d9fa
--- /dev/null
+++ b/source/blender/editors/lineart/lineart_ops.c
@@ -0,0 +1,250 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#include <stdlib.h>
+
+#include "BKE_collection.h"
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "BLI_utildefines.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+
+#include "UI_resources.h"
+
+#include "ED_lineart.h"
+
+#include "lineart_intern.h"
+
+extern LineartSharedResource lineart_share;
+
+static int lineart_gpencil_update_strokes_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Depsgraph *dg = CTX_data_depsgraph_pointer(C);
+
+ BLI_spin_lock(&lineart_share.lock_loader);
+
+ ED_lineart_compute_feature_lines_background(dg, 0);
+
+ /* Wait for loading finish. */
+ BLI_spin_lock(&lineart_share.lock_loader);
+ BLI_spin_unlock(&lineart_share.lock_loader);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int lineart_gpencil_bake_strokes_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneLineArt *lineart = &scene->lineart;
+ Depsgraph *dg = CTX_data_depsgraph_pointer(C);
+ int frame;
+ int frame_begin = ((lineart->flags & LRT_BAKING_FINAL_RANGE) ? MAX2(scene->r.sfra, 1) :
+ lineart->baking_preview_start);
+ int frame_end = ((lineart->flags & LRT_BAKING_FINAL_RANGE) ? scene->r.efra :
+ lineart->baking_preview_end);
+ int frame_total = frame_end - frame_begin;
+ int frame_orig = scene->r.cfra;
+ int frame_increment = ((lineart->flags & LRT_BAKING_KEYFRAMES_ONLY) ?
+ 1 :
+ (lineart->baking_skip + 1));
+ LineartGpencilModifierData *lmd;
+ LineartRenderBuffer *rb;
+ int use_types;
+ bool frame_updated;
+
+ /* Needed for progress report. */
+ lineart_share.wm = CTX_wm_manager(C);
+ lineart_share.main_window = CTX_wm_window(C);
+
+ for (frame = frame_begin; frame <= frame_end; frame += frame_increment) {
+
+ frame_updated = false;
+
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
+ scene->master_collection, ob, DAG_EVAL_RENDER) {
+
+ int cleared = 0;
+ if (ob->type == OB_GPENCIL) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ if (md->type == eGpencilModifierType_Lineart) {
+ lmd = (LineartGpencilModifierData *)md;
+ bGPdata *gpd = ob->data;
+ bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
+ bGPDframe *gpf = ((lineart->flags & LRT_BAKING_KEYFRAMES_ONLY) ?
+ BKE_gpencil_layer_frame_find(gpl, frame) :
+ BKE_gpencil_layer_frame_get(gpl, frame, GP_GETFRAME_ADD_NEW));
+
+ if (!gpf) {
+ continue; /* happens when it's keyframe only. */
+ }
+
+ if (!frame_updated) {
+ /* Reset flags. LRT_SYNC_IGNORE prevent any line art modifiers run calculation
+ * function when depsgraph calls for modifier evalurates. */
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_IGNORE, false);
+ ED_lineart_calculation_flag_set(LRT_RENDER_IDLE);
+
+ BKE_scene_frame_set(scene, frame);
+ BKE_scene_graph_update_for_newframe(dg);
+
+ ED_lineart_update_render_progress(
+ (int)((float)(frame - frame_begin) / frame_total * 100), NULL);
+
+ BLI_spin_lock(&lineart_share.lock_loader);
+ ED_lineart_compute_feature_lines_background(dg, 0);
+
+ /* Wait for loading finish. */
+ BLI_spin_lock(&lineart_share.lock_loader);
+ BLI_spin_unlock(&lineart_share.lock_loader);
+
+ while (!ED_lineart_modifier_sync_flag_check(LRT_SYNC_FRESH) ||
+ !ED_lineart_calculation_flag_check(LRT_RENDER_FINISHED)) {
+ /* Wait till it's done. */
+ }
+
+ ED_lineart_chain_clear_picked_flag(lineart_share.render_buffer_shared);
+
+ frame_updated = true;
+ }
+
+ /* Clear original frame. */
+ if ((scene->lineart.flags & LRT_GPENCIL_OVERWRITE) && (!cleared)) {
+ BKE_gpencil_layer_frame_delete(gpl, gpf);
+ gpf = BKE_gpencil_layer_frame_get(gpl, frame, GP_GETFRAME_ADD_NEW);
+ cleared = 1;
+ }
+
+ rb = lineart_share.render_buffer_shared;
+
+ if (rb->fuzzy_everything) {
+ use_types = LRT_EDGE_FLAG_CONTOUR;
+ }
+ else if (rb->fuzzy_intersections) {
+ use_types = lmd->line_types | LRT_EDGE_FLAG_INTERSECTION;
+ }
+ else {
+ use_types = lmd->line_types;
+ }
+
+ ED_lineart_gpencil_generate_with_type(
+ dg,
+ ob,
+ gpl,
+ gpf,
+ lmd->source_type,
+ lmd->source_type == LRT_SOURCE_OBJECT ? (void *)lmd->source_object :
+ (void *)lmd->source_collection,
+ lmd->level_start,
+ lmd->use_multiple_levels ? lmd->level_end : lmd->level_start,
+ lmd->target_material ?
+ BKE_gpencil_object_material_index_get(ob, lmd->target_material) :
+ 0,
+ use_types,
+ lmd->transparency_flags,
+ lmd->transparency_mask,
+ lmd->thickness,
+ lmd->opacity,
+ lmd->pre_sample_length,
+ lmd->source_vertex_group,
+ lmd->vgname,
+ lmd->flags);
+ }
+ }
+ }
+ }
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ }
+
+ /* Restore original frame. */
+ BKE_scene_frame_set(scene, frame_orig);
+ BKE_scene_graph_update_for_newframe(dg);
+
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_IDLE, false);
+ ED_lineart_calculation_flag_set(LRT_RENDER_FINISHED);
+
+ BKE_report(op->reports, RPT_INFO, "Line Art baking is complete.");
+ WM_operator_confirm_message_ex(C,
+ op,
+ "Line Art baking is complete.",
+ ICON_MOD_WIREFRAME,
+ "Disable Line Art master switch",
+ WM_OP_EXEC_REGION_WIN);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL);
+
+ ED_lineart_update_render_progress(100, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int lineart_gpencil_bake_strokes_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+
+ /* If confirmed in the dialog, then just turn off the master switch upon finished baking. */
+ scene->lineart.flags &= (~LRT_AUTO_UPDATE);
+
+ return OPERATOR_FINISHED;
+}
+
+/* Blocking 1 frame update. */
+void SCENE_OT_lineart_update_strokes(wmOperatorType *ot)
+{
+ ot->name = "Update Line Art Strokes";
+ ot->description = "Update strokes for Line Art grease pencil targets";
+ ot->idname = "SCENE_OT_lineart_update_strokes";
+
+ ot->exec = lineart_gpencil_update_strokes_exec;
+}
+
+/* All frames in range. */
+void SCENE_OT_lineart_bake_strokes(wmOperatorType *ot)
+{
+ ot->name = "Bake Line Art Strokes";
+ ot->description = "Bake Line Art into grease pencil strokes for all frames";
+ ot->idname = "SCENE_OT_lineart_bake_strokes";
+
+ ot->invoke = lineart_gpencil_bake_strokes_invoke;
+ ot->exec = lineart_gpencil_bake_strokes_exec;
+}
+
+void ED_operatortypes_lineart(void)
+{
+ WM_operatortype_append(SCENE_OT_lineart_update_strokes);
+ WM_operatortype_append(SCENE_OT_lineart_bake_strokes);
+}
diff --git a/source/blender/editors/lineart/lineart_util.c b/source/blender/editors/lineart/lineart_util.c
new file mode 100644
index 00000000000..7a09cc81c29
--- /dev/null
+++ b/source/blender/editors/lineart/lineart_util.c
@@ -0,0 +1,233 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+/* #include <time.h> */
+#include "ED_lineart.h"
+#include "MEM_guardedalloc.h"
+#include <math.h>
+
+#include "lineart_intern.h"
+
+/* Line art memory and list helper */
+
+void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, void *data)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = lineart_mem_aquire(smp, sizeof(LinkData));
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+void *lineart_list_append_pointer_pool_sized(ListBase *h,
+ LineartStaticMemPool *smp,
+ void *data,
+ int size)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = lineart_mem_aquire(smp, size);
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+
+void *lineart_list_pop_pointer_no_free(ListBase *h)
+{
+ LinkData *lip;
+ void *rev = 0;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = BLI_pophead(h);
+ rev = lip ? lip->data : 0;
+ return rev;
+}
+void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
+{
+ BLI_remlink(h, (void *)lip);
+}
+
+LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size)
+{
+ size_t set_size = size;
+ if (set_size < LRT_MEMORY_POOL_64MB) {
+ set_size = LRT_MEMORY_POOL_64MB; /* Prevent too many small allocations. */
+ }
+ size_t total_size = size + sizeof(LineartStaticMemPoolNode);
+ LineartStaticMemPoolNode *smpn = MEM_callocN(total_size, "mempool");
+ smpn->size = total_size;
+ smpn->used_byte = sizeof(LineartStaticMemPoolNode);
+ BLI_addhead(&smp->pools, smpn);
+ return smpn;
+}
+void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
+{
+ LineartStaticMemPoolNode *smpn = smp->pools.first;
+ void *ret;
+
+ if (!smpn || (smpn->used_byte + size) > smpn->size) {
+ smpn = lineart_mem_new_static_pool(smp, size);
+ }
+
+ ret = ((unsigned char *)smpn) + smpn->used_byte;
+
+ smpn->used_byte += size;
+
+ return ret;
+}
+void *lineart_mem_aquire_thread(LineartStaticMemPool *smp, size_t size)
+{
+ void *ret;
+
+ BLI_spin_lock(&smp->lock_mem);
+
+ LineartStaticMemPoolNode *smpn = smp->pools.first;
+
+ if (!smpn || (smpn->used_byte + size) > smpn->size) {
+ smpn = lineart_mem_new_static_pool(smp, size);
+ }
+
+ ret = ((unsigned char *)smpn) + smpn->used_byte;
+
+ smpn->used_byte += size;
+
+ BLI_spin_unlock(&smp->lock_mem);
+
+ return ret;
+}
+void lineart_mem_destroy(LineartStaticMemPool *smp)
+{
+ LineartStaticMemPoolNode *smpn;
+ while ((smpn = BLI_pophead(&smp->pools)) != NULL) {
+ MEM_freeN(smpn);
+ }
+}
+
+void lineart_prepend_line_direct(LineartRenderLine **first, void *node)
+{
+ LineartRenderLine *ln = (LineartRenderLine *)node;
+ ln->next = (*first);
+ (*first) = ln;
+}
+
+void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link)
+{
+ LinkNode *ln = lineart_mem_aquire_thread(smp, sizeof(LinkNode));
+ ln->next = (*first);
+ ln->link = link;
+ (*first) = ln;
+}
+
+/* =======================================================================[str] */
+
+void lineart_matrix_perspective_44d(
+ double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax)
+{
+ double yMax;
+ double yMin;
+ double xMin;
+ double xMax;
+
+ if (fAspect < 1) {
+ yMax = zMin * tan(fFov_rad * 0.5f);
+ yMin = -yMax;
+ xMin = yMin * fAspect;
+ xMax = -xMin;
+ }
+ else {
+ xMax = zMin * tan(fFov_rad * 0.5f);
+ xMin = -xMax;
+ yMin = xMin / fAspect;
+ yMax = -yMin;
+ }
+
+ unit_m4_db(mProjection);
+
+ mProjection[0][0] = (2.0f * zMin) / (xMax - xMin);
+ mProjection[1][1] = (2.0f * zMin) / (yMax - yMin);
+ mProjection[2][0] = (xMax + xMin) / (xMax - xMin);
+ mProjection[2][1] = (yMax + yMin) / (yMax - yMin);
+ mProjection[2][2] = -((zMax + zMin) / (zMax - zMin));
+ mProjection[2][3] = -1.0f;
+ mProjection[3][2] = -((2.0f * (zMax * zMin)) / (zMax - zMin));
+ mProjection[3][3] = 0.0f;
+}
+void lineart_matrix_ortho_44d(double (*mProjection)[4],
+ double xMin,
+ double xMax,
+ double yMin,
+ double yMax,
+ double zMin,
+ double zMax)
+{
+ unit_m4_db(mProjection);
+
+ mProjection[0][0] = 2.0f / (xMax - xMin);
+ mProjection[1][1] = 2.0f / (yMax - yMin);
+ mProjection[2][2] = -2.0f / (zMax - zMin);
+ mProjection[3][0] = -((xMax + xMin) / (xMax - xMin));
+ mProjection[3][1] = -((yMax + yMin) / (yMax - yMin));
+ mProjection[3][2] = -((zMax + zMin) / (zMax - zMin));
+ mProjection[3][3] = 1.0f;
+}
+
+void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb)
+{
+ size_t total = 0;
+ size_t sum_this = 0;
+ size_t count_this = 0;
+
+ LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &rb->render_data_pool.pools) {
+ count_this++;
+ sum_this += LRT_MEMORY_POOL_64MB;
+ }
+ printf("LANPR Memory allocated %lu Standalone nodes, total %lu Bytes.\n", count_this, sum_this);
+ total += sum_this;
+ sum_this = 0;
+ count_this = 0;
+
+ LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->line_buffer_pointers) {
+ count_this++;
+ sum_this += reln->element_count * sizeof(LineartRenderLine);
+ }
+ printf(" allocated %lu edge blocks, total %lu Bytes.\n", count_this, sum_this);
+ total += sum_this;
+ sum_this = 0;
+ count_this = 0;
+
+ LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
+ count_this++;
+ sum_this += reln->element_count * rb->triangle_size;
+ }
+ printf(" allocated %lu triangle blocks, total %lu Bytes.\n", count_this, sum_this);
+ total += sum_this;
+ sum_this = 0;
+ count_this = 0;
+}
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index 35bf295a678..0a8dbfcc0e0 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -85,6 +85,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_BULLET)
add_definitions(-DWITH_BULLET)
endif()
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 68ac2842bab..4933c6823e3 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -7746,7 +7746,7 @@ void MESH_OT_symmetry_snap(struct wmOperatorType *ot)
/** \} */
-#ifdef WITH_FREESTYLE
+#if (defined(WITH_FREESTYLE) || defined(WITH_LINEART))
/* -------------------------------------------------------------------- */
/** \name Mark Edge (Freestyle) Operator
@@ -7906,7 +7906,7 @@ void MESH_OT_mark_freestyle_face(wmOperatorType *ot)
/** \} */
-#endif /* WITH_FREESTYLE */
+#endif /* WITH_FREESTYLE || WITH_LINEART */
/* -------------------------------------------------------------------- */
/** \name Loop Normals Editing Tools Modal Map
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 21feddfb886..f9c8bd415dd 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -267,7 +267,7 @@ void MESH_OT_paint_mask_slice(struct wmOperatorType *ot);
struct wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf);
-#ifdef WITH_FREESTYLE
+#if (defined(WITH_FREESTYLE) || defined(WITH_LINEART))
void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot);
void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot);
#endif
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index 27d73497b49..88b2b23ee0b 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -130,7 +130,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_loop_multi_select);
WM_operatortype_append(MESH_OT_mark_seam);
WM_operatortype_append(MESH_OT_mark_sharp);
-#ifdef WITH_FREESTYLE
+#if (defined(WITH_FREESTYLE) || defined(WITH_LINEART))
WM_operatortype_append(MESH_OT_mark_freestyle_edge);
#endif
WM_operatortype_append(MESH_OT_vertices_smooth);
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 77b5379ddd4..e1cfdf45aee 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -86,6 +86,10 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GEOMETRY_NODES)
add_definitions(-DWITH_POINT_CLOUD)
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index dc941f12eba..9c9d748cf3f 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -31,6 +31,7 @@
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
+#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
#include "DNA_light_types.h"
@@ -68,6 +69,7 @@
#include "BKE_geometry_set.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
+#include "BKE_gpencil_modifier.h"
#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
@@ -1305,7 +1307,7 @@ static bool object_gpencil_add_poll(bContext *C)
static int object_gpencil_add_exec(bContext *C, wmOperator *op)
{
- Object *ob = CTX_data_active_object(C);
+ Object *ob = CTX_data_active_object(C), *ob_orig = ob;
bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL;
const int type = RNA_enum_get(op->ptr, "type");
@@ -1333,6 +1335,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
ob_name = "Stroke";
break;
}
+#ifdef WITH_LINEART
+ case GP_LRT_OBJECT:
+ case GP_LRT_COLLECTION: {
+ ob_name = "Line Art";
+ break;
+ }
+#endif
default: {
break;
}
@@ -1373,6 +1382,52 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
ED_gpencil_create_monkey(C, ob, mat);
break;
}
+#ifdef WITH_LINEART
+ case GP_LRT_SCENE:
+ case GP_LRT_COLLECTION:
+ case GP_LRT_OBJECT: {
+ float radius = RNA_float_get(op->ptr, "radius");
+ float mat[4][4];
+
+ ED_object_new_primitive_matrix(C, ob, loc, rot, mat);
+ mul_v3_fl(mat[0], radius);
+ mul_v3_fl(mat[1], radius);
+ mul_v3_fl(mat[2], radius);
+
+ ED_gpencil_create_lineart(C, ob);
+
+ gpd = ob->data;
+
+ /* Add Line Art modifier */
+ LineartGpencilModifierData *md = (LineartGpencilModifierData *)BKE_gpencil_modifier_new(
+ eGpencilModifierType_Lineart);
+ BLI_addtail(&ob->greasepencil_modifiers, md);
+ BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, (GpencilModifierData *)md);
+
+ if (type == GP_LRT_COLLECTION) {
+ md->source_type = LRT_SOURCE_COLLECTION;
+ md->source_collection = CTX_data_collection(C);
+ }
+ else if (type == GP_LRT_OBJECT) {
+ md->source_type = LRT_SOURCE_OBJECT;
+ md->source_object = ob_orig;
+ }
+ else {
+ /* Whole scene. */
+ md->source_type = LRT_SOURCE_SCENE;
+ }
+ /* Only created one layer and one material. */
+ strcpy(md->target_layer, ((bGPDlayer *)gpd->layers.first)->info);
+ md->target_material = BKE_gpencil_material(ob, 1);
+
+ /* Stroke object is drawn in front of meshes by default. */
+ ob->dtx |= OB_DRAW_IN_FRONT;
+
+ /* Turn on Line Art auto update to show the result. */
+ Scene *scene = CTX_data_scene(C);
+ scene->lineart.flags |= LRT_AUTO_UPDATE;
+ }
+#endif
case GP_EMPTY:
/* do nothing */
break;
@@ -1393,6 +1448,45 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static const EnumPropertyItem *object_gpencil_add_options(bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ EnumPropertyItem *item = NULL, *item_ref = rna_enum_object_gpencil_type_items;
+ int totitem = 0;
+ int i = 0;
+ int orig_count = RNA_enum_items_count(item_ref);
+
+ /* Default types. */
+ for (i = 0; i < orig_count; i++) {
+ if (item_ref[i].value == GP_LRT_OBJECT || item_ref[i].value == GP_LRT_COLLECTION ||
+ item_ref[i].value == GP_LRT_SCENE) {
+#ifdef WITH_LINEART
+ if (item_ref[i].value == GP_LRT_SCENE) {
+ /* separator before line art types */
+ RNA_enum_item_add_separator(&item, &totitem);
+ }
+ else if (item_ref[i].value == GP_LRT_OBJECT) {
+ Object *ob = CTX_data_active_object(C);
+ if (!ob || ob->type != OB_MESH) {
+ continue;
+ }
+ }
+#else
+ /* Don't show line art options when not compiled with one. */
+ continue;
+#endif
+ }
+ RNA_enum_item_add(&item, &totitem, &item_ref[i]);
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+
void OBJECT_OT_gpencil_add(wmOperatorType *ot)
{
/* identifiers */
@@ -1413,6 +1507,7 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
ED_object_add_generic_props(ot, false);
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", "");
+ RNA_def_enum_funcs(ot->prop, object_gpencil_add_options);
}
/** \} */
diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt
index 46afa390997..67f552bd15c 100644
--- a/source/blender/editors/render/CMakeLists.txt
+++ b/source/blender/editors/render/CMakeLists.txt
@@ -66,6 +66,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 4ed1cbd60a5..3260a31bcbe 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -56,6 +56,10 @@
#include "ED_render.h"
#include "ED_view3d.h"
+#ifdef WITH_LINEART
+# include "ED_lineart.h"
+#endif
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -98,6 +102,15 @@ void ED_render_view3d_update(Depsgraph *depsgraph,
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
+#ifdef WITH_LINEART
+ /* TODO: Needs some specific flag in the render pipeline that marks line art for update, that
+ * way we could avoid some unecessary updating.
+ */
+ if (updated) {
+ ED_lineart_post_frame_update_external(C, scene, depsgraph, false);
+ }
+#endif
+
engine->flag &= ~RE_ENGINE_DO_UPDATE;
/* NOTE: Important to pass non-updated depsgraph, This is because this function is called
* from inside dependency graph evaluation. Additionally, if we pass fully evaluated one
diff --git a/source/blender/editors/space_api/CMakeLists.txt b/source/blender/editors/space_api/CMakeLists.txt
index 85c07223f2d..8af28baa5c2 100644
--- a/source/blender/editors/space_api/CMakeLists.txt
+++ b/source/blender/editors/space_api/CMakeLists.txt
@@ -58,4 +58,8 @@ set(LIB
bf_editor_space_view3d
)
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
blender_add_lib(bf_editor_space_api "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index adb824b8934..48e4c4ce575 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -68,6 +68,10 @@
#include "ED_util.h"
#include "ED_uvedit.h"
+#ifdef WITH_LINEART
+# include "ED_lineart.h"
+#endif
+
#include "io_ops.h"
/* Only called once on startup. storage is global in BKE kernel listbase. */
@@ -127,6 +131,10 @@ void ED_spacetypes_init(void)
ED_operatortypes_view2d();
ED_operatortypes_ui();
+#ifdef WITH_LINEART
+ ED_operatortypes_lineart();
+#endif
+
ED_screen_user_menu_register();
/* Gizmo types. */
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index c71e5e49d8d..d173adafeaa 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -55,4 +55,8 @@ if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_HAIR_NODES)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 3efaff72637..dc3548d08fb 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -33,6 +33,7 @@
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
+#include "DNA_collection_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
@@ -153,6 +154,29 @@ static bool buttons_context_path_world(ButsContextPath *path)
return false;
}
+static bool buttons_context_path_collection(ButsContextPath *path, wmWindow *window)
+{
+ PointerRNA *ptr = &path->ptr[path->len - 1];
+
+ /* if we already have a (pinned) collection, we're done */
+ if (RNA_struct_is_a(ptr->type, &RNA_Collection)) {
+ return true;
+ }
+ /* if we have a view layer, use the view layer's active collection */
+ else if (buttons_context_path_view_layer(path, window)) {
+ ViewLayer *view_layer = path->ptr[path->len - 1].data;
+ Collection *c = view_layer->active_collection->collection;
+ if (c) {
+ RNA_id_pointer_create(&c->id, &path->ptr[path->len]);
+ path->len++;
+ return true;
+ }
+ }
+
+ /* no path to a collection possible */
+ return false;
+}
+
static bool buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window)
{
PointerRNA *ptr = &path->ptr[path->len - 1];
@@ -575,6 +599,13 @@ static bool buttons_context_path(
case BCONTEXT_WORLD:
found = buttons_context_path_world(path);
break;
+ case BCONTEXT_COLLECTION: /* This is for Line Art collection flags */
+#ifdef WITH_LINEART
+ found = buttons_context_path_collection(path, window);
+#else
+ BLI_assert(!"'WITH_LINEART' is off.");
+#endif
+ break;
case BCONTEXT_TOOL:
found = true;
break;
@@ -858,6 +889,10 @@ int /*eContextResult*/ buttons_context(const bContext *C,
set_pointer_type(path, result, &RNA_World);
return CTX_RESULT_OK;
}
+ if (CTX_data_equals(member, "collection")) {
+ set_pointer_type(path, result, &RNA_Collection);
+ return 1;
+ }
if (CTX_data_equals(member, "object")) {
set_pointer_type(path, result, &RNA_Object);
return CTX_RESULT_OK;
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 07bc1d42c3f..e1cb84108ae 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -194,6 +194,10 @@ int ED_buttons_tabs_list(SpaceProperties *sbuts, short *context_tabs_array)
context_tabs_array[length] = BCONTEXT_VIEW_LAYER;
length++;
}
+ if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) {
+ context_tabs_array[length] = BCONTEXT_COLLECTION;
+ length++;
+ }
if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) {
context_tabs_array[length] = BCONTEXT_SCENE;
length++;
@@ -271,6 +275,12 @@ static const char *buttons_main_region_context_string(const short mainb)
return "view_layer";
case BCONTEXT_WORLD:
return "world";
+ case BCONTEXT_COLLECTION:
+#ifdef WITH_LINEART
+ return "collection";
+#else
+ BLI_assert(!"'WITH_LINEART' is off.");
+#endif
case BCONTEXT_OBJECT:
return "object";
case BCONTEXT_DATA:
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index 9242fc15021..0f1d16e18b3 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -91,6 +91,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_XR_OPENXR)
add_definitions(-DWITH_XR_OPENXR)
endif()
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 3d260a9a05b..bb5ad957448 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1149,6 +1149,11 @@ static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeP
}
WM_msg_subscribe_rna_anon_type(mbus, SceneEEVEE, &msg_sub_value_region_tag_redraw);
+
+#ifdef WITH_LINEART
+ WM_msg_subscribe_rna_anon_type(mbus, SceneLineArt, &msg_sub_value_region_tag_redraw);
+#endif
+
WM_msg_subscribe_rna_anon_type(mbus, SceneDisplay, &msg_sub_value_region_tag_redraw);
WM_msg_subscribe_rna_anon_type(mbus, ObjectDisplay, &msg_sub_value_region_tag_redraw);
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 7d7d10004a3..dd00f5bebba 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -65,6 +65,7 @@ set(SRC
../include/ED_keyframes_draw.h
../include/ED_keyframes_edit.h
../include/ED_keyframing.h
+ ../include/ED_lineart.h
../include/ED_lattice.h
../include/ED_markers.h
../include/ED_mask.h
@@ -114,6 +115,10 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
list(APPEND INC
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 9903711834a..7318a14ab8c 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -50,6 +50,7 @@
#include "ED_armature.h"
#include "ED_image.h"
+#include "ED_lineart.h"
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_paint.h"
@@ -174,6 +175,11 @@ void ED_editors_init(bContext *C)
SWAP(int, reports->flag, reports_flag_prev);
wm->op_undo_depth--;
+
+ /* Line Art data lock duing async calculation */
+#ifdef WITH_LINEART
+ ED_lineart_init_locks();
+#endif
}
/* frees all editmode stuff */
@@ -231,6 +237,11 @@ void ED_editors_exit(Main *bmain, bool do_undo_system)
/* global in meshtools... */
ED_mesh_mirror_spatial_table_end(NULL);
ED_mesh_mirror_topo_table_end(NULL);
+
+ /* Line Art data*/
+#ifdef WITH_LINEART
+ ED_lineart_destroy_render_data_external();
+#endif
}
bool ED_editors_flush_edits_for_object_ex(Main *bmain,
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 9f1484b47c0..f9cc7737f19 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -53,6 +53,7 @@ set(SRC
intern/MOD_gpencilbuild.c
intern/MOD_gpencilcolor.c
intern/MOD_gpencilhook.c
+ intern/MOD_gpencillineart.c
intern/MOD_gpencillattice.c
intern/MOD_gpencilmirror.c
intern/MOD_gpencilmultiply.c
@@ -79,6 +80,11 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
+add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
index 3f167ac6785..e6ce7983a0f 100644
--- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
+++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
@@ -43,6 +43,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Armature;
extern GpencilModifierTypeInfo modifierType_Gpencil_Time;
extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply;
extern GpencilModifierTypeInfo modifierType_Gpencil_Texture;
+extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart;
/* MOD_gpencil_util.c */
void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index f5bfe66562b..beb32c7a38e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -74,6 +74,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
INIT_GP_TYPE(Time);
INIT_GP_TYPE(Multiply);
INIT_GP_TYPE(Texture);
+ INIT_GP_TYPE(Lineart);
#undef INIT_GP_TYPE
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
index cc8eae64300..f021f71dbdc 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
@@ -179,7 +179,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object || mmd->object->type != OB_ARMATURE;
}
-static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
{
ArmatureGpencilModifierData *lmd = (ArmatureGpencilModifierData *)md;
if (lmd->object != NULL) {
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
index b8fa88327fc..7aa1320c4e3 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
@@ -318,7 +318,9 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
generate_geometry(md, depsgraph, scene, ob);
}
-static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
{
ArrayGpencilModifierData *lmd = (ArrayGpencilModifierData *)md;
if (lmd->object != NULL) {
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
index 09cce3f1ab4..e0300eb498f 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
@@ -330,7 +330,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object;
}
-static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
{
HookGpencilModifierData *lmd = (HookGpencilModifierData *)md;
if (lmd->object != NULL) {
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
index b2a83e83c9e..4be80bcf1c5 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
@@ -195,7 +195,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object || mmd->object->type != OB_LATTICE;
}
-static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
{
LatticeGpencilModifierData *lmd = (LatticeGpencilModifierData *)md;
if (lmd->object != NULL) {
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
new file mode 100644
index 00000000000..6f2c6d12edc
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -0,0 +1,484 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017, Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <stdio.h>
+
+#include "BLI_utildefines.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math_vector.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_collection_types.h"
+#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_lineart_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "ED_lineart.h"
+
+#include "BKE_collection.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_lib_query.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_modifier.h"
+#include "RNA_access.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
+#include "MOD_gpencil_util.h"
+
+#include "ED_lineart.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+extern LineartSharedResource lineart_share;
+
+static void initData(GpencilModifierData *md)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ lmd->line_types = LRT_EDGE_FLAG_ALL_TYPE;
+ lmd->thickness = 25;
+ lmd->opacity = 1.0f;
+ lmd->flags |= LRT_GPENCIL_MATCH_OUTPUT_VGROUP;
+}
+
+static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
+{
+ BKE_gpencil_modifier_copydata_generic(md, target);
+}
+
+static void generate_strokes_actual(
+ GpencilModifierData *md, Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+
+ if (G.debug_value == 4000) {
+ printf("LRT: Generating from modifier.\n");
+ }
+
+ ED_lineart_gpencil_generate_with_type(
+ depsgraph,
+ ob,
+ gpl,
+ gpf,
+ lmd->source_type,
+ lmd->source_type == LRT_SOURCE_OBJECT ? (void *)lmd->source_object :
+ (void *)lmd->source_collection,
+ lmd->level_start,
+ lmd->use_multiple_levels ? lmd->level_end : lmd->level_start,
+ lmd->target_material ? BKE_gpencil_object_material_index_get(ob, lmd->target_material) : 0,
+ lmd->line_types,
+ lmd->transparency_flags,
+ lmd->transparency_mask,
+ lmd->thickness,
+ lmd->opacity,
+ lmd->pre_sample_length,
+ lmd->source_vertex_group,
+ lmd->vgname,
+ lmd->flags);
+}
+
+static bool isModifierDisabled(GpencilModifierData *md)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+
+ if ((lmd->target_layer[0] == '\0') || (lmd->target_material == NULL)) {
+ return true;
+ }
+
+ if (lmd->source_type == LRT_SOURCE_OBJECT && !lmd->source_object) {
+ return true;
+ }
+
+ if (lmd->source_type == LRT_SOURCE_COLLECTION && !lmd->source_collection) {
+ return true;
+ }
+
+ return false;
+}
+static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ bGPdata *gpd = ob->data;
+
+ Scene *s = DEG_get_evaluated_scene(depsgraph);
+ if (!(s->lineart.flags & LRT_AUTO_UPDATE)) {
+ return;
+ }
+
+ /* Guard early, don't trigger calculation when no gpencil frame is present. Probably should
+ * disable in the isModifierDisabled() function but we need addtional arg for depsgraph and
+ * gpd. */
+ bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
+ if (gpl == NULL) {
+ return;
+ }
+ /* Need to call this or we don't get active frame (user may haven't selected any one). */
+ BKE_gpencil_frame_active_set(depsgraph, gpd);
+ bGPDframe *gpf = gpl->actframe;
+ if (gpf == NULL) {
+ return;
+ }
+
+ bool is_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+
+ if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_IGNORE)) {
+ return;
+ }
+
+ /* Check all parameters required are filled. */
+ if (isModifierDisabled(md)) {
+ return;
+ }
+
+ if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_IDLE)) {
+ /* Update triggered when nothing's happening, means DG update, so we request a refresh on line
+ * art cache, meanwhile waiting for result. Update will trigger again. */
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_WAITING, true);
+ /* Don't have data yet, update line art. Note: ED_lineart_post_frame_update_external will
+ * automatically return when calculation is already in progress.*/
+ if (is_render) {
+
+ if (G.debug_value == 4000) {
+ printf("LRT: -------- Modifier calls for update when idle.\n");
+ }
+ ED_lineart_post_frame_update_external(
+ NULL, DEG_get_evaluated_scene(depsgraph), depsgraph, true);
+ while (!ED_lineart_modifier_sync_flag_check(LRT_SYNC_FRESH) ||
+ !ED_lineart_calculation_flag_check(LRT_RENDER_FINISHED)) {
+ /* Wait till it's done. */
+ }
+ }
+ else {
+ return;
+ }
+ }
+ else if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_WAITING)) {
+ if (G.debug_value == 4000) {
+ printf("LRT: -------- Modifier is waiting for data in LRT_SYNC_WAITING.\n");
+ }
+ /* Calculation in process. */
+ if (is_render) {
+ while (!ED_lineart_modifier_sync_flag_check(LRT_SYNC_FRESH) ||
+ !ED_lineart_calculation_flag_check(LRT_RENDER_FINISHED)) {
+ /* Wait till it's done. */
+ }
+ }
+ else {
+ return;
+ }
+ }
+
+ if (G.debug_value == 4000) {
+ printf("(is_render == %d) ", is_render);
+ }
+
+ /* If we reach here, means calculation is finished (LRT_SYNC_FRESH), we grab cache. flag reset is
+ * done by calculation function.*/
+ generate_strokes_actual(md, depsgraph, ob, gpl, gpf);
+
+ WM_main_add_notifier(NA_EDITED | NC_GPENCIL, NULL);
+}
+
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
+{
+
+ bGPdata *gpd = ob->data;
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+
+ bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
+ if (gpl == NULL) {
+ return;
+ }
+ bGPDframe *gpf = gpl->actframe;
+ if (gpf == NULL) {
+ return;
+ }
+
+ if (scene->lineart.flags & LRT_AUTO_UPDATE) {
+ if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_IDLE)) {
+ /* Need to run it once again. */
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_WAITING, true);
+ BLI_spin_lock(&lineart_share.lock_loader);
+ ED_lineart_compute_feature_lines_background(depsgraph, 1);
+ /* Wait for loading finish. */
+ BLI_spin_lock(&lineart_share.lock_loader);
+ BLI_spin_unlock(&lineart_share.lock_loader);
+ }
+ while (!ED_lineart_modifier_sync_flag_check(LRT_SYNC_FRESH) ||
+ !ED_lineart_calculation_flag_check(LRT_RENDER_FINISHED)) {
+ /* Wait till it's done. */
+ }
+ }
+ else if (!ED_lineart_modifier_sync_flag_check(LRT_SYNC_FRESH) ||
+ !ED_lineart_modifier_sync_flag_check(LRT_SYNC_IDLE)) {
+ /* If not auto updating, and the cache isn't available, then do not generate strokes. */
+ return;
+ }
+
+ generate_strokes_actual(md, depsgraph, ob, gpl, gpf);
+}
+
+static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
+{
+ return isModifierDisabled(md);
+}
+
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int mode)
+{
+ DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ if (lmd->source_type == LRT_SOURCE_OBJECT && lmd->source_object) {
+ DEG_add_object_relation(
+ ctx->node, lmd->source_object, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
+ DEG_add_object_relation(
+ ctx->node, lmd->source_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+ }
+ else {
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->scene->master_collection, ob, mode) {
+ if (ob->type == OB_MESH || ob->type == OB_MBALL || ob->type == OB_CURVE ||
+ ob->type == OB_SURF || ob->type == OB_FONT) {
+ if (!(ob->lineart.usage & COLLECTION_LRT_EXCLUDE)) {
+ DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
+ DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+ }
+ }
+ }
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ }
+ DEG_add_object_relation(
+ ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+}
+
+static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+
+ walk(userData, ob, (ID **)&lmd->target_material, IDWALK_CB_USER);
+ walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP);
+
+ walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP);
+}
+
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ Scene *scene = CTX_data_scene(C);
+
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
+
+ int source_type = RNA_enum_get(ptr, "source_type");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "source_type", 0, NULL, ICON_NONE);
+
+ if (source_type == LRT_SOURCE_OBJECT) {
+ uiItemR(layout, ptr, "source_object", 0, NULL, ICON_CUBE);
+ }
+ else if (source_type == LRT_SOURCE_COLLECTION) {
+ uiItemR(layout, ptr, "source_collection", 0, NULL, ICON_OUTLINER_COLLECTION);
+ }
+
+ if (scene->lineart.flags & LRT_EVERYTHING_AS_CONTOUR) {
+ uiItemL(layout, "Line types are fuzzy", ICON_NONE);
+ }
+ else {
+ uiLayout *column = uiLayoutColumn(layout, true);
+ if (scene->lineart.line_types & LRT_EDGE_FLAG_CONTOUR) {
+ uiItemR(column, ptr, "use_contour", 0, NULL, ICON_NONE);
+ }
+ if (scene->lineart.line_types & LRT_EDGE_FLAG_CREASE) {
+ uiItemR(column, ptr, "use_crease", 0, "Crease", ICON_NONE);
+ }
+ if (scene->lineart.line_types & LRT_EDGE_FLAG_MATERIAL) {
+ uiItemR(column, ptr, "use_material", 0, "Material", ICON_NONE);
+ }
+ if (scene->lineart.line_types & LRT_EDGE_FLAG_EDGE_MARK) {
+ uiItemR(column, ptr, "use_edge_mark", 0, "Edge Marks", ICON_NONE);
+ }
+ if (scene->lineart.flags & LRT_INTERSECTION_AS_CONTOUR) {
+ uiItemL(column, "Intersection is fuzzy", ICON_NONE);
+ }
+ else {
+ if (scene->lineart.line_types & LRT_EDGE_FLAG_INTERSECTION) {
+ uiItemR(column, ptr, "use_intersection", 0, "Intersection", ICON_NONE);
+ }
+ }
+ }
+
+ uiItemPointerR(layout, ptr, "target_layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL);
+ uiItemPointerR(
+ layout, ptr, "target_material", &obj_data_ptr, "materials", NULL, ICON_SHADING_TEXTURE);
+
+ gpencil_modifier_panel_end(layout, ptr);
+}
+
+static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayout *layout = panel->layout;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayout *column = uiLayoutColumn(layout, true);
+
+ uiItemR(column, ptr, "thickness", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ uiItemR(column, ptr, "opacity", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ uiItemR(column, ptr, "pre_sample_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+}
+
+static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayout *layout = panel->layout;
+
+ uiLayoutSetPropSep(layout, true);
+
+ bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
+ bool use_transparency = RNA_boolean_get(ptr, "use_transparency");
+
+ uiItemR(layout, ptr, "use_multiple_levels", 0, "Multiple Levels", ICON_NONE);
+
+ if (use_multiple_levels) {
+ uiLayout *col = uiLayoutColumn(layout, true);
+ uiItemR(col, ptr, "level_start", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "level_end", 0, NULL, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, ptr, "level_start", 0, "Level", ICON_NONE);
+ }
+
+ uiItemR(layout, ptr, "use_transparency", 0, "Transparency", ICON_NONE);
+
+ uiLayout *column = uiLayoutColumn(layout, true);
+
+ if (use_transparency) {
+ uiItemR(column, ptr, "transparency_match", 0, "Match", ICON_NONE);
+ }
+
+ if (use_transparency) {
+ uiLayout *row = uiLayoutRow(column, true);
+ uiItemR(row, ptr, "transparency_mask_0", UI_ITEM_R_TOGGLE, "0", ICON_NONE);
+ uiItemR(row, ptr, "transparency_mask_1", UI_ITEM_R_TOGGLE, "1", ICON_NONE);
+ uiItemR(row, ptr, "transparency_mask_2", UI_ITEM_R_TOGGLE, "2", ICON_NONE);
+ uiItemR(row, ptr, "transparency_mask_3", UI_ITEM_R_TOGGLE, "3", ICON_NONE);
+ uiItemR(row, ptr, "transparency_mask_4", UI_ITEM_R_TOGGLE, "4", ICON_NONE);
+ uiItemR(row, ptr, "transparency_mask_5", UI_ITEM_R_TOGGLE, "5", ICON_NONE);
+ uiItemR(row, ptr, "transparency_mask_6", UI_ITEM_R_TOGGLE, "6", ICON_NONE);
+ uiItemR(row, ptr, "transparency_mask_7", UI_ITEM_R_TOGGLE, "7", ICON_NONE);
+ }
+}
+
+static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ uiLayout *layout = panel->layout, *row;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayout *column = uiLayoutColumn(layout, true);
+
+ row = uiLayoutRow(column, true);
+ uiItemR(row, ptr, "source_vertex_group", 0, "Filter Source", ICON_GROUP_VERTEX);
+ uiItemR(row, ptr, "invert_source_vertex_group", UI_ITEM_R_TOGGLE, "", ICON_ARROW_LEFTRIGHT);
+
+ uiItemR(column, ptr, "match_output_vertex_group", 0, NULL, ICON_NONE);
+
+ bool match_output = RNA_boolean_get(ptr, "match_output_vertex_group");
+ if (!match_output) {
+ uiItemPointerR(column, ptr, "vertex_group", &ob_ptr, "vertex_groups", "Target", ICON_NONE);
+ }
+
+ uiItemR(layout, ptr, "soft_selection", 0, NULL, ICON_NONE);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Lineart, panel_draw);
+
+ gpencil_modifier_subpanel_register(
+ region_type, "style", "Style", NULL, style_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "occlusion", "Occlusion", NULL, occlusion_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type);
+}
+
+GpencilModifierTypeInfo modifierType_Gpencil_Lineart = {
+ /* name. */ "Line Art",
+ /* structName. */ "LineartGpencilModifierData",
+ /* structSize. */ sizeof(LineartGpencilModifierData),
+ /* type. */ eGpencilModifierTypeType_Gpencil,
+ /* flags. */ eGpencilModifierTypeFlag_SupportsEditmode,
+
+ /* copyData. */ copyData,
+
+ /* deformStroke. */ NULL,
+ /* generateStrokes. */ generateStrokes,
+ /* bakeModifier. */ bakeModifier,
+ /* remapTime. */ NULL,
+
+ /* initData. */ initData,
+ /* freeData. */ NULL,
+ /* isDisabled. */ isDisabled,
+ /* updateDepsgraph. */ updateDepsgraph,
+ /* dependsOnTime. */ NULL,
+ /* foreachIDLink. */ foreachIDLink,
+ /* foreachTexLink. */ NULL,
+ /* panelRegister. */ panelRegister,
+};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
index 111c60436bf..eec4eab3441 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
@@ -207,7 +207,9 @@ static bool isDisabled(GpencilModifierData *UNUSED(md), int UNUSED(userRenderPar
return false;
}
-static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
{
MirrorGpencilModifierData *lmd = (MirrorGpencilModifierData *)md;
if (lmd->object != NULL) {
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
index 311d08238b9..fcc0a1f35eb 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
@@ -311,7 +311,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object;
}
-static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
{
TintGpencilModifierData *lmd = (TintGpencilModifierData *)md;
if (lmd->object != NULL) {
diff --git a/source/blender/makesdna/DNA_collection_types.h b/source/blender/makesdna/DNA_collection_types.h
index 0a80e00d456..f5fcb0b190e 100644
--- a/source/blender/makesdna/DNA_collection_types.h
+++ b/source/blender/makesdna/DNA_collection_types.h
@@ -46,6 +46,14 @@ typedef struct CollectionChild {
struct Collection *collection;
} CollectionChild;
+enum CollectionFeatureLine_Usage {
+ COLLECTION_LRT_INCLUDE = 0,
+ COLLECTION_LRT_OCCLUSION_ONLY = (1 << 0),
+ COLLECTION_LRT_EXCLUDE = (1 << 1),
+ COLLECTION_LRT_INTERSECTION_ONLY = (1 << 2),
+ COLLECTION_LRT_NO_INTERSECTION = (1 << 3),
+};
+
typedef struct Collection {
ID id;
@@ -63,8 +71,10 @@ typedef struct Collection {
/* Runtime-only, always cleared on file load. */
short tag;
+ /** Line Art engine specific */
+ short lineart_usage;
+
int16_t color_tag;
- char _pad[2];
/* Runtime. Cache of objects in this collection and all its
* children. This is created on demand when e.g. some physics
@@ -72,6 +82,9 @@ typedef struct Collection {
* collections due to memory usage reasons. */
ListBase object_cache;
+ /* Need this for line art sub-collection selections. */
+ ListBase object_cache_instanced;
+
/* Runtime. List of collections that are a parent of this
* datablock. */
ListBase parents;
@@ -89,6 +102,7 @@ enum {
COLLECTION_RESTRICT_RENDER = (1 << 3), /* Disable in renders. */
COLLECTION_HAS_OBJECT_CACHE = (1 << 4), /* Runtime: object_cache is populated. */
COLLECTION_IS_MASTER = (1 << 5), /* Is master collection embedded in the scene. */
+ COLLECTION_HAS_OBJECT_CACHE_INSTANCED = (1 << 6), /* for object_cache_instanced. */
};
/* Collection->tag */
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index b2d62e0a5f6..24560d89533 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -53,6 +53,7 @@ typedef enum GpencilModifierType {
eGpencilModifierType_Time = 16,
eGpencilModifierType_Multiply = 17,
eGpencilModifierType_Texture = 18,
+ eGpencilModifierType_Lineart = 19,
/* Keep last. */
NUM_GREASEPENCIL_MODIFIER_TYPES,
} GpencilModifierType;
@@ -809,6 +810,59 @@ typedef enum eTextureGpencil_Mode {
STROKE_AND_FILL = 2,
} eTextureGpencil_Mode;
+typedef enum eLineartGpencilModifierSource {
+ LRT_SOURCE_COLLECTION = 0,
+ LRT_SOURCE_OBJECT = 1,
+ LRT_SOURCE_SCENE = 2,
+} eLineartGpencilModifierSource;
+
+typedef enum eLineArtGPencilModifierFlags {
+ LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 0),
+ LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1),
+ LRT_GPENCIL_SOFT_SELECTION = (1 << 2),
+} eLineArtGPencilModifierFlags;
+
+typedef enum eLineartGpencilTransparencyFlags {
+ LRT_GPENCIL_TRANSPARENCY_ENABLE = (1 << 0),
+ /** Set to true means using "and" instead of "or" logic on mask bits. */
+ LRT_GPENCIL_TRANSPARENCY_MATCH = (1 << 1),
+} eLineartGpencilTransparencyFlags;
+
+typedef struct LineartGpencilModifierData {
+ GpencilModifierData modifier;
+
+ short line_types; /* line type enable flags, bits in eLineartEdgeFlag */
+
+ char source_type; /* Object or Collection, from eLineartGpencilModifierSource */
+
+ char use_multiple_levels;
+ short level_start;
+ short level_end;
+
+ struct Object *source_object;
+ struct Collection *source_collection;
+
+ struct Material *target_material;
+ char target_layer[64];
+
+ /** These two variables are to pass on vertex group information from mesh to strokes.
+ * vgname specifies which vertex groups our strokes from source_vertex_group will go to. */
+ char source_vertex_group[64];
+ char vgname[64];
+
+ float opacity;
+ short thickness;
+
+ unsigned char transparency_flags; /* eLineartGpencilTransparencyFlags */
+ unsigned char transparency_mask;
+
+ float pre_sample_length;
+
+ /* Additional Switches */
+ int flags;
+
+} LineartGpencilModifierData;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 9d969a29add..48e3261a1e8 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -412,6 +412,8 @@ typedef enum eGPDframe_Flag {
GP_FRAME_PAINT = (1 << 0),
/* for editing in Action Editor */
GP_FRAME_SELECT = (1 << 1),
+ /* Line Art generation */
+ GP_FRAME_LRT_CLEARED = (1 << 2),
} eGPDframe_Flag;
/* ***************************************** */
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
new file mode 100644
index 00000000000..f5d6b26193e
--- /dev/null
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -0,0 +1,70 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2010 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __DNA_LRT_TYPES_H__
+#define __DNA_LRT_TYPES_H__
+
+/** \file DNA_lineart_types.h
+ * \ingroup DNA
+ */
+
+#include "DNA_ID.h"
+#include "DNA_listBase.h"
+
+struct Object;
+struct Material;
+
+/* Notice that we need to have this file although no struct defines.
+ * Edge flags and usage flags are used by with scene/object/gpencil modifier bits, and those values
+ * needs to stay consistent throughout. */
+
+typedef enum eLineartEdgeFlag {
+ LRT_EDGE_FLAG_EDGE_MARK = (1 << 0),
+ LRT_EDGE_FLAG_CONTOUR = (1 << 1),
+ LRT_EDGE_FLAG_CREASE = (1 << 2),
+ LRT_EDGE_FLAG_MATERIAL = (1 << 3),
+ LRT_EDGE_FLAG_INTERSECTION = (1 << 4),
+ /** floating edge, unimplemented yet */
+ LRT_EDGE_FLAG_FLOATING = (1 << 5),
+ /** also used as discarded line mark */
+ LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 6),
+ LRT_EDGE_FLAG_CLIPPED = (1 << 7),
+ /* Maxed out for 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */
+} eLineartEdgeFlag;
+
+#define LRT_EDGE_FLAG_ALL_TYPE 0x3f
+
+typedef enum eLineartModeFlags {
+ LRT_LINE_LAYER_USE_SAME_STYLE = (1 << 0), /* Share with object lineart flags */
+ LRT_LINE_LAYER_USE_MULTIPLE_LEVELS = (1 << 1), /* Share with object lineart flags */
+ LRT_LINE_LAYER_NORMAL_ENABLED = (1 << 2),
+ LRT_LINE_LAYER_NORMAL_INVERSE = (1 << 3),
+ LRT_LINE_LAYER_REPLACE_STROKES = (1 << 4),
+ LRT_LINE_LAYER_COLLECTION_FORCE = (1 << 5),
+} eLineartModeFlags;
+
+#endif
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index 884c2df6480..55d5ea202f7 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -145,6 +145,16 @@ typedef enum eMaterialGPencilStyle_Mode {
GP_MATERIAL_MODE_SQUARE = 2,
} eMaterialGPencilStyle_Mode;
+typedef struct MaterialLineArt {
+ int flags; /* eMaterialLineArtFlags */
+ unsigned char transparency_mask;
+ unsigned char _pad[3];
+} MaterialLineArt;
+
+typedef enum eMaterialLineArtFlags {
+ LRT_MATERIAL_TRANSPARENCY_ENABLED = (1 << 0),
+} eMaterialLineArtFlags;
+
typedef struct Material {
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
@@ -210,6 +220,7 @@ typedef struct Material {
/** Grease pencil color. */
struct MaterialGPencilStyle *gp_style;
+ struct MaterialLineArt lineart;
} Material;
/* **************** MATERIAL ********************* */
diff --git a/source/blender/makesdna/DNA_object_defaults.h b/source/blender/makesdna/DNA_object_defaults.h
index 1bca572b963..d545b7912c0 100644
--- a/source/blender/makesdna/DNA_object_defaults.h
+++ b/source/blender/makesdna/DNA_object_defaults.h
@@ -66,6 +66,7 @@
.preview = NULL, \
.duplicator_visibility_flag = OB_DUPLI_FLAG_VIEWPORT | OB_DUPLI_FLAG_RENDER, \
.pc_ids = {NULL, NULL}, \
+ .lineart = { .crease_threshold = DEG2RAD(140.0f) }, \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index f6372a0c240..fa4e3953f5b 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -26,6 +26,11 @@
#include "DNA_object_enums.h"
+#include "DNA_customdata_types.h"
+#include "DNA_defs.h"
+#include "DNA_lineart_types.h"
+#include "DNA_listBase.h"
+
#include "DNA_ID.h"
#include "DNA_action_types.h" /* bAnimVizSettings */
#include "DNA_customdata_types.h"
@@ -200,6 +205,27 @@ typedef struct Object_Runtime {
short _pad2[3];
} Object_Runtime;
+typedef struct ObjectLineArt {
+ short usage;
+ short flags;
+
+ /** if OBJECT_LRT_OWN_CREASE is set */
+ float crease_threshold;
+} ObjectLineArt;
+
+enum ObjectFeatureLine_Usage {
+ OBJECT_LRT_INHERENT = 0,
+ OBJECT_LRT_INCLUDE = (1 << 0),
+ OBJECT_LRT_OCCLUSION_ONLY = (1 << 1),
+ OBJECT_LRT_EXCLUDE = (1 << 2),
+ OBJECT_LRT_INTERSECTION_ONLY = (1 << 3),
+ OBJECT_LRT_NO_INTERSECTION = (1 << 4),
+};
+
+enum ObjectFeatureLine_Flags {
+ OBJECT_LRT_OWN_CREASE = (1 << 0),
+};
+
typedef struct Object {
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
@@ -405,6 +431,8 @@ typedef struct Object {
struct PreviewImage *preview;
+ ObjectLineArt lineart;
+
/** Runtime evaluation data (keep last). */
Object_Runtime runtime;
} Object;
@@ -595,6 +623,9 @@ enum {
GP_EMPTY = 0,
GP_STROKE = 1,
GP_MONKEY = 2,
+ GP_LRT_SCENE = 3,
+ GP_LRT_OBJECT = 4,
+ GP_LRT_COLLECTION = 5,
};
/* boundtype */
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 3e1b2ef56a1..e055870e6ab 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -238,6 +238,17 @@
SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION | \
SCE_EEVEE_SSR_HALF_RESOLUTION | SCE_EEVEE_SHADOW_SOFT, \
}
+
+
+
+#define _DNA_DEFAULT_SceneLineArt \
+ { \
+ .crease_threshold = DEG2RAD(140.0f),\
+ .angle_splitting_threshold = DEG2RAD(60.0f),\
+ .chaining_geometry_threshold = 0.001f,\
+ .chaining_image_threshold = 0.001f,\
+ }
+
#define _DNA_DEFAULT_Scene \
{ \
@@ -252,6 +263,7 @@
.safe_areas = _DNA_DEFAULT_DisplaySafeAreas, \
\
.eevee = _DNA_DEFAULT_SceneEEVEE, \
+ .lineart = _DNA_DEFAULT_SceneLineArt, \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c7f7e610a1a..6f505778266 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -36,6 +36,7 @@
#include "DNA_color_types.h" /* color management */
#include "DNA_customdata_types.h" /* Scene's runtime cddata masks. */
#include "DNA_layer_types.h"
+#include "DNA_lineart_types.h"
#include "DNA_listBase.h"
#include "DNA_vec_types.h"
#include "DNA_view3d_types.h"
@@ -1658,6 +1659,62 @@ typedef struct SceneEEVEE {
float light_threshold;
} SceneEEVEE;
+/* Line Art Global Settings */
+
+typedef enum eLineartPostProcessingStatus {
+ LRT_POST_PROCESSING_DISABLED = 0,
+ LRT_POST_PROCESSING_ENABLED = 1,
+} eLineartPostProcessingStatus;
+
+typedef enum eLineartMainFlags {
+ LRT_ENABLED = (1 << 0), /* Deprecated right now. */
+ /* For Line Art->GP and viewport to update automatically. */
+ LRT_AUTO_UPDATE = (1 << 1),
+ LRT_SAME_TAPER = (1 << 2),
+ /* Edge split modifier will cause problems in Line Art. */
+ LRT_DISABLE_EDGE_SPLITS = (1 << 3),
+ LRT_USE_CHAINING = (1 << 4), /* Deprecated */
+ LRT_USE_INTERSECTIONS = (1 << 5), /* Deprecated, use flags in line_types */
+ /* Overwrite existing strokes in this frame. */
+ LRT_GPENCIL_OVERWRITE = (1 << 6),
+ LRT_INTERSECTION_AS_CONTOUR = (1 << 7),
+ LRT_EVERYTHING_AS_CONTOUR = (1 << 8),
+ LRT_ALLOW_DUPLI_OBJECTS = (1 << 9),
+ LRT_ALLOW_OVERLAPPING_EDGES = (1 << 10),
+ LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 11),
+ LRT_BAKING_FINAL_RANGE = (1 << 12),
+ LRT_BAKING_KEYFRAMES_ONLY = (1 << 13),
+ LRT_REMOVE_DOUBLES = (1 << 14),
+} eLineartMainFlags;
+
+typedef struct SceneLineArt {
+ int flags;
+
+ /** line_types is used to select line types in global scope, especially when Fuzzy chaining is
+ * enabled. See DNA_lineart_types.h for edge flags.
+ */
+ int line_types;
+
+ /* Shared */
+ /** Reserved for suggestive contour */
+ float contour_fade;
+
+ /** 0-1 range for cosine angle */
+ float crease_threshold;
+
+ /** 0-PI angle, for splitting strokes at sharp points */
+ float angle_splitting_threshold;
+
+ /* CPU mode */
+ float chaining_geometry_threshold;
+ float chaining_image_threshold;
+
+ /* Baking */
+ int baking_preview_start;
+ int baking_preview_end;
+ int baking_skip; /* 0 for every frame, 1 for every other frame and so on. */
+} SceneLineArt;
+
typedef struct SceneGpencil {
float smaa_threshold;
char _pad[4];
@@ -1795,6 +1852,7 @@ typedef struct Scene {
struct SceneDisplay display;
struct SceneEEVEE eevee;
struct SceneGpencil grease_pencil_settings;
+ struct SceneLineArt lineart;
} Scene;
/* **************** RENDERDATA ********************* */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 98f652b04f8..8fa2951e3d7 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -225,6 +225,7 @@ typedef enum eSpaceButtons_Context {
BCONTEXT_TOOL = 14,
BCONTEXT_SHADERFX = 15,
BCONTEXT_OUTPUT = 16,
+ BCONTEXT_COLLECTION = 17,
/* Keep last. */
BCONTEXT_TOT,
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 7624649bf78..26fc56cfa1d 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -84,6 +84,7 @@ static const char *includefiles[] = {
"DNA_mesh_types.h",
"DNA_meshdata_types.h",
"DNA_modifier_types.h",
+ "DNA_lineart_types.h",
"DNA_lattice_types.h",
"DNA_object_types.h",
"DNA_object_force_types.h",
@@ -1558,6 +1559,7 @@ int main(int argc, char **argv)
#include "DNA_layer_types.h"
#include "DNA_light_types.h"
#include "DNA_lightprobe_types.h"
+#include "DNA_lineart_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_listBase.h"
#include "DNA_mask_types.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 74b8517f538..8e3c94a3ff9 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -454,6 +454,7 @@ extern StructRNA RNA_NormalEditModifier;
extern StructRNA RNA_Object;
extern StructRNA RNA_ObjectBase;
extern StructRNA RNA_ObjectDisplay;
+extern StructRNA RNA_ObjectLineArt;
extern StructRNA RNA_OceanModifier;
extern StructRNA RNA_OceanTexData;
extern StructRNA RNA_OffsetGpencilModifier;
@@ -515,6 +516,7 @@ extern StructRNA RNA_SPHFluidSettings;
extern StructRNA RNA_Scene;
extern StructRNA RNA_SceneDisplay;
extern StructRNA RNA_SceneEEVEE;
+extern StructRNA RNA_SceneLineArt;
extern StructRNA RNA_SceneObjects;
extern StructRNA RNA_SceneRenderLayer;
extern StructRNA RNA_SceneSequence;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 94cfd8464ae..141fe7945d0 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -331,6 +331,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_OPENSUBDIV)
list(APPEND INC
../../../../intern/opensubdiv
@@ -443,6 +447,13 @@ set(LIB
bf_editor_undo
)
+if(WITH_LINEART)
+ list(APPEND LIB
+ bf_editor_lineart
+ )
+endif()
+
+add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_rna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c
index 20a455f5312..b0aa3ddfb54 100644
--- a/source/blender/makesrna/intern/rna_collection.c
+++ b/source/blender/makesrna/intern/rna_collection.c
@@ -22,8 +22,12 @@
#include "DNA_collection_types.h"
+#include "DNA_lineart_types.h"
+
#include "BLI_utildefines.h"
+#include "MEM_guardedalloc.h"
+
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -517,6 +521,31 @@ void RNA_def_collections(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update");
+ static const EnumPropertyItem rna_collection_lineart_usage[] = {
+ {COLLECTION_LRT_INCLUDE, "INCLUDE", 0, "Include", "Collection will produce feature lines"},
+ {COLLECTION_LRT_OCCLUSION_ONLY,
+ "OCCLUSION_ONLY",
+ 0,
+ "Occlusion Only",
+ "Only use the collection to produce occlusion"},
+ {COLLECTION_LRT_EXCLUDE, "EXCLUDE", 0, "Exclude", "Don't use this collection in LRT"},
+ {COLLECTION_LRT_INTERSECTION_ONLY,
+ "INTERSECTION_ONLY",
+ 0,
+ "Intersection Only",
+ "Only generate intersection lines with this collection"},
+ {COLLECTION_LRT_NO_INTERSECTION,
+ "NO_INTERSECTION",
+ 0,
+ "No Intersection",
+ "Do not generate intersection lines for this collection"},
+ {0, NULL, 0, NULL, NULL}};
+
+ prop = RNA_def_property(srna, "lineart_usage", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_collection_lineart_usage);
+ RNA_def_property_ui_text(prop, "Usage", "How to use this collection in LRT");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
prop = RNA_def_property(srna, "color_tag", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "color_tag");
RNA_def_property_enum_funcs(
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 71d5a53adb2..1a385a03fc6 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -88,6 +88,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_SUBSURF,
"Subdivide",
"Subdivide stroke adding more control points"},
+ {eGpencilModifierType_Lineart,
+ "GP_LINEART",
+ ICON_MOD_EDGESPLIT,
+ "Line Art",
+ "Generate Line Art strokes from selected source"},
{0, "", 0, N_("Deform"), ""},
{eGpencilModifierType_Armature,
"GP_ARMATURE",
@@ -241,6 +246,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
return &RNA_MultiplyGpencilModifier;
case eGpencilModifierType_Texture:
return &RNA_TextureGpencilModifier;
+ case eGpencilModifierType_Lineart:
+ return &RNA_LineartGpencilModifier;
/* Default */
case eGpencilModifierType_None:
case NUM_GREASEPENCIL_MODIFIER_TYPES:
@@ -307,10 +314,11 @@ RNA_GP_MOD_VGROUP_NAME_SET(Opacity, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Lattice, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Smooth, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Hook, vgname);
+RNA_GP_MOD_VGROUP_NAME_SET(Tint, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Offset, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Armature, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Texture, vgname);
-RNA_GP_MOD_VGROUP_NAME_SET(Tint, vgname);
+RNA_GP_MOD_VGROUP_NAME_SET(Lineart, vgname);
# undef RNA_GP_MOD_VGROUP_NAME_SET
@@ -2305,6 +2313,220 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
}
+static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem modifier_lineart_source_type[] = {
+ {LRT_SOURCE_COLLECTION, "COLLECTION", 0, "Collection", ""},
+ {LRT_SOURCE_OBJECT, "OBJECT", 0, "Object", ""},
+ {LRT_SOURCE_SCENE, "SCENE", 0, "Scene", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "LineartGpencilModifier", "GpencilModifier");
+ RNA_def_struct_ui_text(
+ srna, "Line Art Modifier", "Genreate Line Art strokes from selected source");
+ RNA_def_struct_sdna(srna, "LineartGpencilModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
+
+ prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, modifier_lineart_source_type);
+ RNA_def_property_ui_text(prop, "Source Type", "Lineart stroke source type");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "source_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(
+ prop, "Source Object", "Source object that this modifier grabs data from");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "source_collection", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_struct_type(prop, "Collection");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(
+ prop, "Source Collection", "Source collection that this modifier uses data from");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ /* types */
+ prop = RNA_def_property(srna, "use_contour", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_CONTOUR);
+ RNA_def_property_ui_text(prop, "Use Contour", "Include contour lines in the result");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_crease", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_CREASE);
+ RNA_def_property_ui_text(prop, "Use Crease", "Include sharp edges in the result");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_material", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_MATERIAL);
+ RNA_def_property_ui_text(
+ prop, "Use Material", "Include material separation lines in the result");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_edge_mark", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_EDGE_MARK);
+ RNA_def_property_ui_text(prop, "Use Edge Mark", "Include freestyle edge marks in the result");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_intersection", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_INTERSECTION);
+ RNA_def_property_ui_text(prop, "Use Intersection", "Include intersection lines in the result");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_multiple_levels", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_multiple_levels", 0);
+ RNA_def_property_ui_text(
+ prop, "Use Multiple Levels", "Select lines from multiple occlusion levels");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "level_start", PROP_INT, PROP_NONE);
+ RNA_def_property_int_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop, "Level Start", "Minimum level of occlusion level that gets selected");
+ RNA_def_property_range(prop, 0, 128);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE);
+ RNA_def_property_int_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop, "Level End", "Maximum level of occlusion level that gets selected");
+ RNA_def_property_range(prop, 0, 128);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "target_material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(
+ prop, "Target Material", "Grease Pencil material assigned to the generated strokes");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop, "Target Layer", "Grease Pencil layer assigned to the generated strokes");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "source_vertex_group", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop,
+ "Source Vertex Group",
+ "Matches the beginning of vertex group names from mesh objects, match all when left empty");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "vgname");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_LineartGpencilModifier_vgname_set");
+ RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for selected strokes");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_source_vertex_group", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_INVERT_SOURCE_VGROUP);
+ RNA_def_property_ui_text(prop, "Invert Source", "Invert source vertex group values");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "match_output_vertex_group", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_MATCH_OUTPUT_VGROUP);
+ RNA_def_property_ui_text(prop, "Match Output", "Match output vertex group");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "soft_selection", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_SOFT_SELECTION);
+ RNA_def_property_ui_text(
+ prop, "Soft selection", "Preserve original vertex weight instead of clipping to 0/1");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE);
+ RNA_def_property_int_default(prop, 20);
+ RNA_def_property_ui_text(prop, "Thickness", "The thickness that used to generate strokes");
+ RNA_def_property_ui_range(prop, 1, 100, 1, 1);
+ RNA_def_property_range(prop, 1, 200);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_default(prop, 0.1f);
+ RNA_def_property_ui_text(prop, "Opacity", "The strength value used to generate strokes");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 2);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "pre_sample_length", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 0.0f);
+ RNA_def_property_ui_text(prop, "Pre Sample Length", "Sample strokes before sending out");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 2);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_transparency", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_flags", LRT_GPENCIL_TRANSPARENCY_ENABLE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop, "Use Transparency", "Use transparency mask from this material in Line Art");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_match", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_flags", LRT_GPENCIL_TRANSPARENCY_MATCH);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(prop, "Match Transparency", "Match all transparency bits");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_0", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 0);
+ RNA_def_property_ui_text(prop, "Mask 0", "Mask bit 0");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_1", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 1);
+ RNA_def_property_ui_text(prop, "Mask 1", "Mask bit 1");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_2", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 2);
+ RNA_def_property_ui_text(prop, "Mask 2", "Mask bit 2");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_3", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 3);
+ RNA_def_property_ui_text(prop, "Mask 3", "Mask bit 3");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_4", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 4);
+ RNA_def_property_ui_text(prop, "Mask 4", "Mask bit 4");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_5", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 5);
+ RNA_def_property_ui_text(prop, "Mask 5", "Mask bit 5");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_6", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 6);
+ RNA_def_property_ui_text(prop, "mask 6", "Mask bit 6");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_7", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 7);
+ RNA_def_property_ui_text(prop, "Mask 7", "Mask bit 7");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+}
+
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2379,6 +2601,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
rna_def_modifier_gpencilarmature(brna);
rna_def_modifier_gpencilmultiply(brna);
rna_def_modifier_gpenciltexture(brna);
+ rna_def_modifier_gpencillineart(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 84c831a178e..a6b2015c666 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -87,6 +87,7 @@ const EnumPropertyItem rna_enum_ramp_blend_items[] = {
# include "ED_gpencil.h"
# include "ED_image.h"
+# include "ED_lineart.h"
# include "ED_node.h"
# include "ED_screen.h"
@@ -127,6 +128,14 @@ static void rna_MaterialGpencil_update(Main *bmain, Scene *scene, PointerRNA *pt
WM_main_add_notifier(NC_GPENCIL | ND_DATA, ma);
}
+static void rna_MaterialLineArt_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ rna_Material_update(bmain, scene, ptr);
+ if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_IDLE)) {
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_WAITING, false);
+ }
+}
+
static void rna_Material_draw_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Material *ma = (Material *)ptr->owner_id;
@@ -671,6 +680,70 @@ static void rna_def_material_greasepencil(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible");
}
+static void rna_def_material_lineart(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "MaterialLineArt", NULL);
+ RNA_def_struct_sdna(srna, "MaterialLineArt");
+ RNA_def_struct_ui_text(srna, "Material Line Art", "");
+
+ prop = RNA_def_property(srna, "use_transparency", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_MATERIAL_TRANSPARENCY_ENABLED);
+ RNA_def_property_ui_text(
+ prop, "Use Transparency", "Use transparency mask from this material in Line Art");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_0", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 0);
+ RNA_def_property_ui_text(prop, "Mask 0", "Mask bit 0");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_1", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 1);
+ RNA_def_property_ui_text(prop, "Mask 1", "Mask bit 1");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_2", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 2);
+ RNA_def_property_ui_text(prop, "Mask 2", "Mask bit 2");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_3", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 3);
+ RNA_def_property_ui_text(prop, "Mask 3", "Mask bit 3");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_4", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 4);
+ RNA_def_property_ui_text(prop, "Mask 4", "Mask bit 4");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_5", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 5);
+ RNA_def_property_ui_text(prop, "Mask 5", "Mask bit 5");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_6", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 6);
+ RNA_def_property_ui_text(prop, "mask 6", "Mask bit 6");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+
+ prop = RNA_def_property(srna, "transparency_mask_7", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 7);
+ RNA_def_property_ui_text(prop, "Mask 7", "Mask bit 7");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
+}
void RNA_def_material(BlenderRNA *brna)
{
@@ -836,7 +909,13 @@ void RNA_def_material(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Is Grease Pencil", "True if this material has grease pencil data");
+ /* line art */
+ prop = RNA_def_property(srna, "lineart", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "lineart");
+ RNA_def_property_ui_text(prop, "Line Art Settings", "Line Art settings for material");
+
rna_def_material_greasepencil(brna);
+ rna_def_material_lineart(brna);
RNA_api_material(srna);
}
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index a70b776b07a..09727428024 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -36,6 +36,7 @@
#include "DNA_shader_fx_types.h"
#include "DNA_workspace_types.h"
+#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -159,6 +160,21 @@ const EnumPropertyItem rna_enum_object_gpencil_type_items[] = {
{GP_EMPTY, "EMPTY", ICON_EMPTY_AXIS, "Blank", "Create an empty grease pencil object"},
{GP_STROKE, "STROKE", ICON_STROKE, "Stroke", "Create a simple stroke with basic colors"},
{GP_MONKEY, "MONKEY", ICON_MONKEY, "Monkey", "Construct a Suzanne grease pencil object"},
+ {GP_LRT_SCENE,
+ "LRT_SCENE",
+ ICON_SCENE,
+ "Scene Line Art",
+ "Quickly set up Line Art for the whole scene"},
+ {GP_LRT_COLLECTION,
+ "LRT_COLLECTION",
+ ICON_OUTLINER_COLLECTION,
+ "Collection Line Art",
+ "Quickly set up Line Art for active collection"},
+ {GP_LRT_OBJECT,
+ "LRT_OBJECT",
+ ICON_CUBE,
+ "Object Line Art",
+ "Quickly set up Line Art for active collection"},
{0, NULL, 0, NULL, NULL}};
static const EnumPropertyItem parent_type_items[] = {
@@ -2083,6 +2099,12 @@ int rna_Object_use_dynamic_topology_sculpting_get(PointerRNA *ptr)
return (ss && ss->bm);
}
+static void rna_object_lineart_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ptr->owner_id);
+}
+
#else
static void rna_def_vertex_group(BlenderRNA *brna)
@@ -2644,6 +2666,58 @@ static void rna_def_object_display(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
+static void rna_def_object_lineart(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem prop_feature_line_usage_items[] = {
+ {OBJECT_LRT_INHERENT,
+ "INHEREIT",
+ 0,
+ "Inhereit",
+ "Follow settings from the parent collection"},
+ {OBJECT_LRT_INCLUDE, "INCLUDE", 0, "Include", "Include this object into LRT calculation"},
+ {OBJECT_LRT_OCCLUSION_ONLY,
+ "OCCLUSION_ONLY",
+ 0,
+ "Occlusion Only",
+ "Don't produce lines, only used as occlusion object"},
+ {OBJECT_LRT_EXCLUDE, "EXCLUDE", 0, "Exclude", "Don't use this object for LRT rendering"},
+ {OBJECT_LRT_INTERSECTION_ONLY,
+ "INTERSECTION_ONLY",
+ 0,
+ "Intersection Only",
+ "Only to generate intersection lines with this object"},
+ {OBJECT_LRT_NO_INTERSECTION,
+ "NO_INTERSECTION",
+ 0,
+ "No Intersection",
+ "Include this object but do not generate intersection lines"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "ObjectLineArt", NULL);
+ RNA_def_struct_ui_text(srna, "Object Line Art", "Object lineart settings");
+ RNA_def_struct_sdna(srna, "ObjectLineArt");
+
+ prop = RNA_def_property(srna, "usage", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_feature_line_usage_items);
+ RNA_def_property_ui_text(prop, "Usage", "How to use this object in line art calculation");
+ RNA_def_property_update(prop, 0, "rna_object_lineart_update");
+
+ prop = RNA_def_property(srna, "use_crease_override", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", OBJECT_LRT_OWN_CREASE);
+ RNA_def_property_ui_text(prop, "Own Crease", "Use own crease setting to overwrite scene global");
+ RNA_def_property_update(prop, 0, "rna_object_lineart_update");
+
+ prop = RNA_def_property(srna, "crease_threshold", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, 0, DEG2RAD(180.0f));
+ RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(180.0f), 0.01f, 1);
+ RNA_def_property_ui_text(
+ prop, "Own Crease", "Angles smaller than this will be treated as creases");
+ RNA_def_property_update(prop, 0, "rna_object_lineart_update");
+}
static void rna_def_object(BlenderRNA *brna)
{
@@ -3414,6 +3488,11 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_pointer_funcs(prop, "rna_Object_display_get", NULL, NULL, NULL);
RNA_def_property_ui_text(prop, "Object Display", "Object display settings for 3D viewport");
+ /* Line Art */
+ prop = RNA_def_property(srna, "lineart", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ObjectLineArt");
+ RNA_def_property_ui_text(prop, "Line Art", "Line Art settings for the object");
+
RNA_define_lib_overridable(false);
/* anim */
@@ -3434,6 +3513,7 @@ void RNA_def_object(BlenderRNA *brna)
rna_def_face_map(brna);
rna_def_material_slot(brna);
rna_def_object_display(brna);
+ rna_def_object_lineart(brna);
RNA_define_animate_sdna(true);
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 1ac224b27e4..d1971c9f315 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -24,6 +24,7 @@
#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_layer_types.h"
+#include "DNA_lineart_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
@@ -47,6 +48,7 @@
#include "BKE_volume.h"
#include "ED_gpencil.h"
+#include "ED_lineart.h"
#include "ED_object.h"
#include "RNA_define.h"
@@ -2596,6 +2598,29 @@ static char *rna_UnitSettings_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("unit_settings");
}
+/* lineart */
+
+static void rna_lineart_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
+{
+ if (ED_lineart_modifier_sync_flag_check(LRT_SYNC_IDLE)) {
+ ED_lineart_modifier_sync_flag_set(LRT_SYNC_WAITING, false);
+ }
+}
+
+static void rna_lineart_auto_update_set(PointerRNA *ptr, bool value)
+{
+ SceneLineArt *data = (SceneLineArt *)ptr->data;
+
+ if (value) {
+ data->flags |= LRT_AUTO_UPDATE;
+ }
+ else {
+ data->flags &= (~LRT_AUTO_UPDATE);
+ ED_lineart_calculation_flag_set(LRT_RENDER_CANCELING);
+ ED_lineart_destroy_render_data_external();
+ }
+}
+
static char *rna_FFmpegSettings_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("render.ffmpeg");
@@ -7468,6 +7493,177 @@ static void rna_def_scene_gpencil(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
+static void rna_def_scene_lineart(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SceneLineArt", NULL);
+ RNA_def_struct_sdna(srna, "SceneLineArt");
+ RNA_def_struct_ui_text(srna, "Scene Line Art Config", "Line Art global settings");
+
+ prop = RNA_def_property(srna, "auto_update", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_AUTO_UPDATE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop, "Auto Update", "Automatically update Line Art cache when frame changes");
+
+ RNA_def_property_boolean_funcs(prop, NULL, "rna_lineart_auto_update_set");
+ /* Also use this update callback to trigger the modifier to clear the frame. */
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "gpencil_overwrite", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_OVERWRITE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop,
+ "GPencil Overwrite",
+ "Overwrite existing strokes in existing frames of target GPencil objects");
+
+ prop = RNA_def_property(srna, "fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_INTERSECTION_AS_CONTOUR);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop,
+ "Intersection As Contour",
+ "Treat intersection lines as contour so those lines can be chained together");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "fuzzy_everything", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_EVERYTHING_AS_CONTOUR);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(prop,
+ "All Lines As Contour",
+ "Treat all lines as contour so those lines can be chained together");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "allow_duplication", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_ALLOW_DUPLI_OBJECTS);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_ui_text(
+ prop,
+ "Instanced Objects",
+ "Allow particle objects and face/vertiex duplication to show in line art");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "allow_overlapping_edges", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_ALLOW_OVERLAPPING_EDGES);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_ui_text(prop,
+ "Handle Overlapping Edges",
+ "Allow lines from edge split to show properly, may run slower");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "allow_clipping_boundaries", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_ALLOW_CLIPPING_BOUNDARIES);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_ui_text(
+ prop, "Clipping Boundaries", "Allow lines on near/far clipping plane to be shown");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "crease_threshold", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, 0, DEG2RAD(180.0f));
+ RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(180.0f), 0.01f, 1);
+ RNA_def_property_ui_text(
+ prop, "Crease Threshold", "Angles smaller than this will be treated as creases");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "angle_splitting_threshold", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_default(prop, DEG2RAD(60.0f));
+ RNA_def_property_ui_text(
+ prop, "Angle Splitting", "Angle in screen space below which a stroke is split in two");
+ /* Don't allow value very close to PI, or we get a lot of small segments.*/
+ RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 0.01f, 1);
+ RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f));
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "remove_doubles", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_REMOVE_DOUBLES);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop, "Remove Doubles", "Remove doubles when line art is loading geometries");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ /* types */
+ prop = RNA_def_property(srna, "use_contour", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_CONTOUR);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(
+ prop, "Use Contour", "Include lines from object silhouettes in the result");
+ RNA_def_property_update(prop, 0, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "use_crease", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_CREASE);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(prop, "Use Crease", "Include sharp edges in the result");
+ RNA_def_property_update(prop, 0, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "use_material", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_MATERIAL);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(
+ prop, "Use Material", "Include material separation lines in the result");
+ RNA_def_property_update(prop, 0, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "use_edge_mark", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_EDGE_MARK);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(prop, "Use Edge Mark", "Include freestyle edge marks in the result");
+ RNA_def_property_update(prop, 0, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "use_intersections", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_INTERSECTION);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(
+ prop, "Calculate Intersections", "Calculate intersections lines when enabled");
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ /* Below these two are only for grease pencil, thus no viewport updates. */
+
+ prop = RNA_def_property(srna, "chaining_geometry_threshold", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_default(prop, 0.01f);
+ RNA_def_property_ui_text(prop,
+ "Geometry Threshold",
+ "Segments where their geometric distance between them lower than this "
+ "will be chained together");
+ RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.001f, 3);
+ RNA_def_property_range(prop, 0.0f, 0.5f);
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "chaining_image_threshold", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_default(prop, 0.01f);
+ RNA_def_property_ui_text(
+ prop,
+ "Image Threshold",
+ "Segments with an image distance smaller than this will be chained together");
+ RNA_def_property_ui_range(prop, 0.0f, 0.3f, 0.001f, 4);
+ RNA_def_property_range(prop, 0.0f, 0.3f);
+ RNA_def_property_update(prop, NC_SCENE, "rna_lineart_update");
+
+ prop = RNA_def_property(srna, "baking_final_range", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_BAKING_FINAL_RANGE);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_ui_text(
+ prop, "Final Range", "Bake with the scene frame range instead of a preview range");
+
+ prop = RNA_def_property(srna, "baking_keyframes_only", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_BAKING_KEYFRAMES_ONLY);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(prop, "Keyframes Only", "Only fill in existing gpencil key frames");
+
+ prop = RNA_def_property(srna, "baking_preview_start", PROP_INT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Preview start", "First frame to be baked in preview");
+ RNA_def_property_range(prop, 0, MAXFRAME);
+
+ prop = RNA_def_property(srna, "baking_preview_end", PROP_INT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Preview end", "Last frame to be baked in preview");
+ RNA_def_property_range(prop, 0, MAXFRAME);
+
+ prop = RNA_def_property(srna, "baking_skip", PROP_INT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Skip", "Number of frames to skip per baked frame");
+ RNA_def_property_ui_range(prop, 0, 100, 1, 1);
+}
+
void RNA_def_scene(BlenderRNA *brna)
{
StructRNA *srna;
@@ -7943,6 +8139,11 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "SceneEEVEE");
RNA_def_property_ui_text(prop, "Eevee", "Eevee settings for the scene");
+ /* Line Art */
+ prop = RNA_def_property(srna, "lineart", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SceneLineArt");
+ RNA_def_property_ui_text(prop, "Line Art", "Line Art settings for the scene");
+
/* Grease Pencil */
prop = RNA_def_property(srna, "grease_pencil_settings", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "SceneGpencil");
@@ -7966,6 +8167,7 @@ void RNA_def_scene(BlenderRNA *brna)
rna_def_display_safe_areas(brna);
rna_def_scene_display(brna);
rna_def_scene_eevee(brna);
+ rna_def_scene_lineart(brna);
rna_def_view_layer_aov(brna);
rna_def_view_layer_eevee(brna);
rna_def_scene_gpencil(brna);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index affb17c7d8c..447e11594a7 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -453,13 +453,14 @@ static const EnumPropertyItem buttons_context_items[] = {
{BCONTEXT_OUTPUT, "OUTPUT", ICON_OUTPUT, "Output", "Output Properties"},
{BCONTEXT_VIEW_LAYER, "VIEW_LAYER", ICON_RENDER_RESULT, "View Layer", "View Layer Properties"},
{BCONTEXT_WORLD, "WORLD", ICON_WORLD, "World", "World Properties"},
+ {BCONTEXT_COLLECTION, "COLLECTION", ICON_GROUP, "Collection", "Collection Properties"},
{BCONTEXT_OBJECT, "OBJECT", ICON_OBJECT_DATA, "Object", "Object Properties"},
{BCONTEXT_CONSTRAINT,
"CONSTRAINT",
ICON_CONSTRAINT,
"Constraints",
"Object Constraint Properties"},
- {BCONTEXT_MODIFIER, "MODIFIER", ICON_MODIFIER, "Modifiers", "Modifier Properties"},
+ {BCONTEXT_MODIFIER, "MODIFIER", ICON_MODIFIER, "Modifiers", "Modifiers Properties"},
{BCONTEXT_DATA, "DATA", ICON_NONE, "Data", "Object Data Properties"},
{BCONTEXT_BONE, "BONE", ICON_BONE_DATA, "Bone", "Bone Properties"},
{BCONTEXT_BONE_CONSTRAINT,
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 56ef5c8187a..822ab0bd969 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -209,6 +209,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LINEART)
+ add_definitions(-DWITH_LINEART)
+endif()
+
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index aaceb7b393f..1d8045ff2c8 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -36,6 +36,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{"cycles", NULL},
{"cycles_osl", NULL},
{"freestyle", NULL},
+ {"lineart", NULL},
{"image_cineon", NULL},
{"image_dds", NULL},
{"image_hdr", NULL},
@@ -133,6 +134,12 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
+#ifdef WITH_LINEART
+ SetObjIncref(Py_True);
+#else
+ SetObjIncref(Py_False);
+#endif
+
#ifdef WITH_CINEON
SetObjIncref(Py_True);
#else