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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/__init__.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_collection.py98
-rw-r--r--release/scripts/startup/bl_ui/properties_lineart.py59
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py33
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py14
-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/intern/collection.c47
-rw-r--r--source/blender/blenkernel/intern/gpencil.c40
-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/depsgraph/intern/builder/deg_builder_relations.cc2
-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/mesh/editmesh_tools.c2
-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/object_add.c85
-rw-r--r--source/blender/editors/object/object_ops.c5
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c31
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c10
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt12
-rw-r--r--source/blender/gpencil_modifiers/MOD_gpencil_lineart.h31
-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.c477
-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/gpencil_modifiers/intern/lineart/MOD_lineart.h552
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c980
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c3931
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h113
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c439
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_util.c233
-rw-r--r--source/blender/makesdna/DNA_collection_types.h16
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h13
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h73
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h3
-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_space_types.h1
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c2
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/rna_collection.c27
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c284
-rw-r--r--source/blender/makesrna/intern/rna_material.c78
-rw-r--r--source/blender/makesrna/intern/rna_object.c81
-rw-r--r--source/blender/makesrna/intern/rna_space.c1
-rw-r--r--source/blender/windowmanager/WM_api.h1
62 files changed, 8056 insertions, 28 deletions
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index b3131e64df7..86c376bcb21 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -67,6 +67,7 @@ _modules = [
"properties_scene",
"properties_texture",
"properties_world",
+ "properties_collection",
# Generic Space Modules
#
@@ -104,6 +105,8 @@ import bpy
if bpy.app.build_options.freestyle:
_modules.append("properties_freestyle")
+_modules.append("properties_lineart")
+
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]
diff --git a/release/scripts/startup/bl_ui/properties_collection.py b/release/scripts/startup/bl_ui/properties_collection.py
new file mode 100644
index 00000000000..fa4c3cdb779
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_collection.py
@@ -0,0 +1,98 @@
+# ##### 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 #####
+
+# <pep8 compliant>
+from bpy.types import Panel
+
+
+class CollectionButtonsPanel:
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "collection"
+
+
+def lineart_make_line_type_entry(col, line_type, text_disp, expand, search_from):
+ col.prop(line_type, "use", text=text_disp)
+ if line_type.use and expand:
+ col.prop_search(line_type, "layer", search_from,
+ "layers", icon='GREASEPENCIL')
+ col.prop_search(line_type, "material", search_from,
+ "materials", icon='SHADING_TEXTURE')
+
+
+class COLLECTION_PT_collection_flags(CollectionButtonsPanel, Panel):
+ bl_label = "Restrictions"
+
+ @classmethod
+ def poll(cls, context):
+ vl = context.view_layer
+ vlc = vl.active_layer_collection
+ return (vlc.name != 'Master Collection')
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ collection = context.collection
+ vl = context.view_layer
+ vlc = vl.active_layer_collection
+
+ col = layout.column(align=True)
+ col.prop(collection, "hide_select", text="Selectable", toggle=False, invert_checkbox=True)
+ col.prop(collection, "hide_render", toggle=False)
+
+ col = layout.column(align=True)
+ col.prop(vlc, "holdout", toggle=False)
+ col.prop(vlc, "indirect_only", toggle=False)
+
+
+class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
+ bl_label = "Line Art"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+ collection = context.collection
+
+ row = layout.row()
+ row.prop(collection, "lineart_usage")
+
+class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
+ bl_label = "Instancing"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+ collection = context.collection
+
+ row = layout.row()
+ row.prop(collection, "instance_offset")
+
+classes = (
+ COLLECTION_PT_collection_flags,
+ COLLECTION_PT_lineart_collection,
+ COLLECTION_PT_instancing,
+)
+
+if __name__ == "__main__": # only for live edit.
+ from bpy.utils import register_class
+ for cls in classes:
+ register_class(cls)
diff --git a/release/scripts/startup/bl_ui/properties_lineart.py b/release/scripts/startup/bl_ui/properties_lineart.py
new file mode 100644
index 00000000000..7c4d6c7be0b
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_lineart.py
@@ -0,0 +1,59 @@
+# ##### 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 #####
+
+# <pep8 compliant>
+from bpy.types import Panel
+
+
+class LineartButtonsPanel:
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "object"
+
+
+class OBJECT_PT_lineart(LineartButtonsPanel, Panel):
+ bl_label = "Line Art"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ return (ob.type in {'MESH', 'FONT', 'CURVE', 'SURFACE'})
+
+ def draw(self, context):
+ layout = self.layout
+ lineart = context.object.lineart
+
+ layout.use_property_split = True
+
+ layout.prop(lineart, 'usage')
+ layout.use_property_split = True
+
+ row = layout.row(heading="Override Crease")
+ row.prop(lineart, "use_crease_override", text="")
+ row.prop(lineart, "crease_threshold", slider=True, text="")
+
+
+classes = (
+ OBJECT_PT_lineart,
+)
+
+if __name__ == "__main__": # only for live edit.
+ from bpy.utils import register_class
+ for cls in classes:
+ register_class(cls)
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index 47ab98386f4..ebd91143239 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -274,6 +274,38 @@ class MATERIAL_PT_viewport(MaterialButtonsPanel, Panel):
col.prop(mat, "roughness")
+class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
+ bl_label = "Line Art"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ return mat and not mat.grease_pencil
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material
+ lineart = mat.lineart
+
+ layout.prop(lineart, "use_transparency")
+
+ if lineart.use_transparency:
+
+ layout.label(text="Transparency Masks:")
+
+ row = layout.row(align=True)
+ row.prop(lineart, "transparency_mask_0", text="0", toggle=True)
+ row.prop(lineart, "transparency_mask_1", text="1", toggle=True)
+ row.prop(lineart, "transparency_mask_2", text="2", toggle=True)
+ row.prop(lineart, "transparency_mask_3", text="3", toggle=True)
+ row.prop(lineart, "transparency_mask_4", text="4", toggle=True)
+ row.prop(lineart, "transparency_mask_5", text="5", toggle=True)
+ row.prop(lineart, "transparency_mask_6", text="6", toggle=True)
+ row.prop(lineart, "transparency_mask_7", text="7", toggle=True)
+
+
classes = (
MATERIAL_MT_context_menu,
MATERIAL_UL_matslots,
@@ -282,6 +314,7 @@ classes = (
EEVEE_MATERIAL_PT_surface,
EEVEE_MATERIAL_PT_volume,
EEVEE_MATERIAL_PT_settings,
+ MATERIAL_PT_lineart,
MATERIAL_PT_viewport,
EEVEE_MATERIAL_PT_viewport_settings,
MATERIAL_PT_custom_props,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 2742b75a68b..6980181a725 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -3829,11 +3829,10 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu):
col.operator("mesh.mark_sharp")
col.operator("mesh.mark_sharp", text="Clear Sharp").clear = True
- if render.use_freestyle:
- col.separator()
+ col.separator()
- col.operator("mesh.mark_freestyle_edge").clear = False
- col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
+ col.operator("mesh.mark_freestyle_edge").clear = False
+ col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
col.separator()
@@ -4028,11 +4027,10 @@ class VIEW3D_MT_edit_mesh_edges_data(Menu):
props.use_verts = True
props.clear = True
- if render.use_freestyle:
- layout.separator()
+ layout.separator()
- layout.operator("mesh.mark_freestyle_edge").clear = False
- layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
+ layout.operator("mesh.mark_freestyle_edge").clear = False
+ layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
class VIEW3D_MT_edit_mesh_edges(Menu):
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 b3d0e0c9f98..9e237679795 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 (03/2021).
* * 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/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..3b46672f9cd 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -1663,6 +1663,31 @@ 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 active grease pencil layer.
* \param gpd: Grease pencil data-block
@@ -2421,6 +2446,21 @@ 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.
* \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/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/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/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 68ac2842bab..8bb557509e0 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)
/* -------------------------------------------------------------------- */
/** \name Mark Edge (Freestyle) Operator
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 21feddfb886..763bdf04d83 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)
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..34e9a3f45a5 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)
WM_operatortype_append(MESH_OT_mark_freestyle_edge);
#endif
WM_operatortype_append(MESH_OT_vertices_smooth);
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index dc941f12eba..e4527740164 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,11 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
ob_name = "Stroke";
break;
}
+ case GP_LRT_OBJECT:
+ case GP_LRT_COLLECTION: {
+ ob_name = "Line Art";
+ break;
+ }
default: {
break;
}
@@ -1373,6 +1380,46 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
ED_gpencil_create_monkey(C, ob, mat);
break;
}
+ 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;
+ }
case GP_EMPTY:
/* do nothing */
break;
@@ -1393,6 +1440,41 @@ 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;
+ const EnumPropertyItem *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) {
+ 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;
+ }
+ }
+ }
+ 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 +1495,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/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 57676cf5c76..71bea6bf9e3 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -37,6 +37,8 @@
#include "object_intern.h"
+#include "MOD_gpencil_lineart.h"
+
/* ************************** registration **********************************/
void ED_operatortypes_object(void)
@@ -154,6 +156,9 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy_to_selected);
+ /* grease pencil line art */
+ WM_operatortypes_lineart();
+
/* Shader FX. */
WM_operatortype_append(OBJECT_OT_shaderfx_add);
WM_operatortype_append(OBJECT_OT_shaderfx_remove);
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 3efaff72637..13762b348c2 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 */
+ 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,9 @@ 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 */
+ found = buttons_context_path_collection(path, window);
+ break;
case BCONTEXT_TOOL:
found = true;
break;
@@ -858,6 +885,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..43d45db38e2 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -206,6 +206,14 @@ int ED_buttons_tabs_list(SpaceProperties *sbuts, short *context_tabs_array)
context_tabs_array[length] = -1;
length++;
}
+ if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) {
+ context_tabs_array[length] = BCONTEXT_COLLECTION;
+ length++;
+ }
+ if (length != 0) {
+ context_tabs_array[length] = -1;
+ length++;
+ }
if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) {
context_tabs_array[length] = BCONTEXT_OBJECT;
length++;
@@ -271,6 +279,8 @@ static const char *buttons_main_region_context_string(const short mainb)
return "view_layer";
case BCONTEXT_WORLD:
return "world";
+ case BCONTEXT_COLLECTION:
+ return "collection";
case BCONTEXT_OBJECT:
return "object";
case BCONTEXT_DATA:
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 9f1484b47c0..e3bb0d773a2 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
@@ -70,6 +71,16 @@ set(SRC
MOD_gpencil_modifiertypes.h
intern/MOD_gpencil_ui_common.h
intern/MOD_gpencil_util.h
+
+ # Lineart code
+ intern/lineart/lineart_ops.c
+ intern/lineart/lineart_cpu.c
+ intern/lineart/lineart_chain.c
+ intern/lineart/lineart_util.c
+
+ intern/lineart/lineart_intern.h
+ intern/lineart/MOD_lineart.h
+
)
set(LIB
@@ -79,6 +90,7 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
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_lineart.h b/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h
new file mode 100644
index 00000000000..685f0cb36cb
--- /dev/null
+++ b/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#pragma once
+
+#include "DNA_windowmanager_types.h"
+
+/* Operator types should be in exposed header. */
+void OBJECT_OT_lineart_bake_strokes(struct wmOperatorType *ot);
+void OBJECT_OT_lineart_bake_strokes_all(struct wmOperatorType *ot);
+void OBJECT_OT_lineart_clear(struct wmOperatorType *ot);
+void OBJECT_OT_lineart_clear_all(struct wmOperatorType *ot);
+
+void WM_operatortypes_lineart(void);
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 ff493258b9b..7d78892aa6b 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..7fed771dabe
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -0,0 +1,477 @@
+/*
+ * 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_defaults.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 "lineart/MOD_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 "WM_api.h"
+#include "WM_types.h"
+
+static void initData(GpencilModifierData *md)
+{
+ LineartGpencilModifierData *gpmd = (LineartGpencilModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(LineartGpencilModifierData), modifier);
+}
+
+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");
+ }
+
+ MOD_lineart_gpencil_generate(
+ lmd->render_buffer,
+ 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;
+ }
+
+ /* Preventing calculation in depsgraph when baking frames. */
+ if (lmd->flags & LRT_GPENCIL_IS_BAKED) {
+ return true;
+ }
+
+ return false;
+}
+static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ bGPdata *gpd = ob->data;
+
+ /* 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) {
+ BKE_gpencil_frame_addnew(gpl, DEG_get_evaluated_scene(depsgraph)->r.cfra);
+ return;
+ }
+
+ /* Check all parameters required are filled. */
+ if (isModifierDisabled(md)) {
+ return;
+ }
+
+ MOD_lineart_compute_feature_lines(depsgraph, lmd);
+
+ generate_strokes_actual(md, depsgraph, ob, gpl, gpf);
+
+ MOD_lineart_destroy_render_data(lmd);
+
+ 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;
+
+ 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;
+ }
+
+ MOD_lineart_compute_feature_lines(depsgraph, lmd);
+
+ generate_strokes_actual(md, depsgraph, ob, gpl, gpf);
+
+ MOD_lineart_destroy_render_data(lmd);
+}
+
+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 *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
+
+ const int source_type = RNA_enum_get(ptr, "source_type");
+ const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetEnabled(layout, !is_baked);
+
+ uiItemR(layout, ptr, "source_type", 0, NULL, ICON_NONE);
+
+ if (source_type == LRT_SOURCE_OBJECT) {
+ uiItemR(layout, ptr, "source_object", 0, NULL, ICON_OBJECT_DATA);
+ }
+ else if (source_type == LRT_SOURCE_COLLECTION) {
+ uiItemR(layout, ptr, "source_collection", 0, NULL, ICON_OUTLINER_COLLECTION);
+ }
+ else {
+ /* Source is Scene. */
+ }
+
+ uiLayout *col = uiLayoutColumnWithHeading(layout, true, IFACE_("Edge Types"));
+
+ uiItemR(col, ptr, "use_contour", 0, "Contour", ICON_NONE);
+ uiItemR(col, ptr, "use_material", 0, "Material", ICON_NONE);
+ uiItemR(col, ptr, "use_edge_mark", 0, "Edge Marks", ICON_NONE);
+ uiItemR(col, ptr, "use_intersection", 0, "Intersection", ICON_NONE);
+
+ uiLayout *row = uiLayoutRow(col, true);
+ /* Don't automatically add the "animate property" button. */
+ uiLayoutSetPropDecorate(row, false);
+
+ const bool use_crease = RNA_boolean_get(ptr, "use_crease");
+ uiLayout *sub = uiLayoutRow(row, true);
+ uiItemR(sub, ptr, "use_crease", 0, "Crease", ICON_NONE);
+ sub = uiLayoutRow(sub, true);
+ uiLayoutSetEnabled(sub, use_crease);
+ uiItemR(sub, ptr, "crease_threshold", UI_ITEM_R_SLIDER, "", ICON_NONE);
+ uiItemDecoratorR(row, ptr, "crease_threshold", 0);
+
+ 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);
+
+ uiItemR(layout, ptr, "remove_doubles", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "allow_overlapping_edges", 0, "Overlapping Edges As Contour", ICON_NONE);
+
+ 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;
+
+ const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetEnabled(layout, !is_baked);
+
+ uiItemR(layout, ptr, "thickness", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ uiItemR(layout, ptr, "opacity", 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;
+
+ const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetEnabled(layout, !is_baked);
+
+ const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
+ const 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 *col = uiLayoutColumn(layout, true);
+
+ if (use_transparency) {
+ uiItemR(col, ptr, "transparency_match", 0, "Match", ICON_NONE);
+ }
+
+ if (use_transparency) {
+ uiLayout *row = uiLayoutRow(col, 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 chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayout *layout = panel->layout;
+
+ const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetEnabled(layout, !is_baked);
+
+ uiLayout *col = uiLayoutColumnWithHeading(layout, true, IFACE_("Chain"));
+ uiItemR(col, ptr, "fuzzy_intersections", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "fuzzy_everything", 0, NULL, ICON_NONE);
+
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, ptr, "chaining_geometry_threshold", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "chaining_image_threshold", 0, NULL, ICON_NONE);
+
+ uiItemR(layout, ptr, "pre_sample_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ uiItemR(layout, ptr, "angle_splitting_threshold", UI_ITEM_R_SLIDER, NULL, 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;
+
+ bool is_baked = RNA_boolean_get(ptr, "is_baked");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetEnabled(layout, !is_baked);
+
+ uiLayout *col = uiLayoutColumn(layout, true);
+
+ uiLayout *row = uiLayoutRow(col, 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(col, ptr, "match_output_vertex_group", 0, NULL, ICON_NONE);
+
+ bool match_output = RNA_boolean_get(ptr, "match_output_vertex_group");
+ if (!match_output) {
+ uiItemPointerR(col, ptr, "vertex_group", &ob_ptr, "vertex_groups", "Target", ICON_NONE);
+ }
+
+ uiItemR(layout, ptr, "soft_selection", 0, NULL, ICON_NONE);
+}
+
+static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ bool is_baked = RNA_boolean_get(ptr, "is_baked");
+
+ uiLayoutSetPropSep(layout, true);
+
+ if (is_baked) {
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiLayoutSetPropSep(col, false);
+ uiItemL(col, "Modifier has baked data.", ICON_NONE);
+ uiItemR(col, ptr, "is_baked", UI_ITEM_R_TOGGLE, "Continue Without Clearing", ICON_NONE);
+ }
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiLayoutSetEnabled(col, !is_baked);
+ uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_bake_strokes");
+ uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_bake_strokes_all");
+
+ col = uiLayoutColumn(layout, false);
+ uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear");
+ uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all");
+}
+
+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, "chaining", "Chaining", NULL, chaining_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "baking", "Baking", NULL, baking_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/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
new file mode 100644
index 00000000000..7519f3ef40f
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -0,0 +1,552 @@
+/*
+ * 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
+
+#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>
+
+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 */
+
+ /* Only use single link list, because we don't need to go back in order.
+ * This variable is also reused to store the pointer to adjacent lines of this triangle before
+ * intersection statge */
+ 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.
+ * The size of LineartRenderTriangle is dynamically allocated to contain set thread number of
+ * "testing" field. Worker threads will test lines against the "base" triangle.
+ * At least one thread is present, thus we always have at least testing[0]. */
+ struct LineartRenderLine *testing[1];
+} 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];
+
+ /* Scene global index. */
+ int index;
+
+ /** Intersection data flag is here, when LRT_VERT_HAS_INTERSECTION_DATA is set,
+ * size of the struct is extended to include intersection data.
+ * See eLineArtVertFlags.
+ */
+ 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;
+
+ int thread_count;
+
+ 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];
+
+ 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;
+
+ /* 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 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 allow_overlapping_edges;
+ bool remove_doubles;
+
+ /** Keep an copy of these data so when line art is running it's self-contained. */
+ 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;
+
+#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 {
+ struct LineartRenderBuffer *rb;
+
+ 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;
+ }
+
+ *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
+}
+
+struct Depsgraph;
+struct Scene;
+struct LineartRenderBuffer;
+struct LineartGpencilModifierData;
+
+void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd);
+
+void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb);
+void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb);
+void MOD_lineart_chain_connect(LineartRenderBuffer *rb, const bool do_geometry_space);
+void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
+void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
+
+int MOD_lineart_chain_count(const LineartRenderLineChain *rlc);
+void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb);
+
+int MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph,
+ struct LineartGpencilModifierData *lmd);
+
+struct Scene;
+
+LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *rb,
+ double x,
+ double y);
+
+LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y);
+
+struct bGPDlayer;
+struct bGPDframe;
+struct GpencilModifierData;
+
+void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
+ 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);
+
+float MOD_lineart_chain_compute_length(LineartRenderLineChain *rlc);
+
+struct wmOperatorType;
+
+void ED_operatortypes_lineart(void);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
new file mode 100644
index 00000000000..36115fa1027
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -0,0 +1,980 @@
+/*
+ * 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 "MOD_lineart.h"
+
+#include "bmesh.h"
+
+#include "lineart_intern.h"
+
+#include <math.h>
+
+#define LRT_OTHER_RV(rl, rv) ((rv) == (rl)->l ? (rl)->r : ((rv) == (rl)->r ? (rl)->l : NULL))
+
+/* Get a connected line, only for lines who has the exact given vert, or (in the case of
+ * intersection lines) who has a vert that has the exact same position. */
+static LineartRenderLine *lineart_line_get_connected(LineartBoundingArea *ba,
+ LineartRenderVert *rv,
+ LineartRenderVert **new_rv,
+ int match_flag)
+{
+ LISTBASE_FOREACH (LinkData *, lip, &ba->linked_lines) {
+ LineartRenderLine *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;
+ }
+
+ *new_rv = LRT_OTHER_RV(nrl, rv);
+ if (*new_rv) {
+ return nrl;
+ }
+
+ 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;
+ }
+ 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;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+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 the new chain point is overlapping, just replace the type and occlusion level of the
+ * current point. This makes it so that the line to the point after this one has the correct
+ * type and level. */
+ 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_prepend_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 MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
+{
+ LineartRenderLineChain *rlc;
+ LineartRenderLineChainItem *rlci;
+ LineartBoundingArea *ba;
+ LineartRenderLineSegment *rls;
+ int last_occlusion;
+ unsigned char last_transparency;
+ /* Used when 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)) {
+ LRT_ITER_ALL_LINES_NEXT
+ continue;
+ }
+
+ rl->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
+
+ rlc = lineart_chain_create(rb);
+
+ /* One chain can only have one object_ref,
+ * so we assign it based on the first segment we found. */
+ rlc->object_ref = rl->object_ref;
+
+ 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 = MOD_lineart_get_bounding_area(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_prepend_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_prepend_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_prepend_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_prepend_point(rb,
+ rlc,
+ use_fbcoord,
+ use_gpos,
+ N,
+ new_rl->flags,
+ last_occlusion,
+ last_transparency,
+ new_rl->r_obindex);
+ }
+ ba = MOD_lineart_get_bounding_area(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: Adding all cuts from the given line, so we can continue connecting the right side
+ * of the 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 = MOD_lineart_get_bounding_area(rb, rl->r->fbcoord[0], rl->r->fbcoord[1]);
+ new_rv = rl->r;
+ 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;
+ /* Fix leading vertex occlusion. */
+ rlci->occlusion = last_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 = MOD_lineart_get_bounding_area(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;
+ }
+
+ 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);
+ }
+ if (IN_BOUND(ch[1], rlci)) {
+ return lineart_bounding_area_get_rlci_recursive(rb, &ch[1], rlci);
+ }
+ if (IN_BOUND(ch[2], rlci)) {
+ return lineart_bounding_area_get_rlci_recursive(rb, &ch[2], rlci);
+ }
+ 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 = MOD_lineart_get_parent_bounding_area(rb, rlci->pos[0], rlci->pos[1]);
+ if (root == NULL) {
+ return NULL;
+ }
+ return lineart_bounding_area_get_rlci_recursive(rb, root, rlci);
+}
+
+/* Here we will try to connect geometry space chains together in image space. However we can't
+ * chain two chains together if their end and start points lie on the border between two bounding
+ * areas, this happens either when 1) the geometry is way too dense, or 2) the chaining threshold
+ * is too big that it covers multiple small bounding areas. */
+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 = MOD_lineart_get_parent_bounding_area(rb, pl->pos[0], pl->pos[1]);
+ LineartBoundingArea *ba2 = MOD_lineart_get_parent_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 MOD_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;
+ }
+ }
+ }
+
+ /* We want a closer point anyway. So using modified dist is fine. */
+ float adjacent_new_len = dist;
+ 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 function only connects two different chains. It will not do any clean up or smart chaining.
+ * So no: removing overlapping chains, removal of short isolated segments, and no loop reduction is
+ * implemented yet. */
+void MOD_lineart_chain_connect(LineartRenderBuffer *rb, const bool 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 MOD_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 MOD_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 (MOD_lineart_chain_compute_length(rlc) < threshold) {
+ BLI_remlink(&rb->chains, rlc);
+ }
+ }
+}
+
+int MOD_lineart_chain_count(const LineartRenderLineChain *rlc)
+{
+ int count = 0;
+ LISTBASE_FOREACH (LineartRenderLineChainItem *, rlci, &rlc->chain) {
+ count++;
+ }
+ return count;
+}
+
+void MOD_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
+ * MOD_lineart_chain_split_for_fixed_occlusion().*/
+void MOD_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/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
new file mode 100644
index 00000000000..195f0088114
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -0,0 +1,3931 @@
+/*
+ * 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 "MOD_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 "MOD_gpencil_modifiertypes.h"
+
+#include "lineart_intern.h"
+
+static LineartBoundingArea *lineart_line_first_bounding_area(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 bool 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 bool lineart_triangle_line_image_space_occlusion(SpinLock *spl,
+ const LineartRenderTriangle *rt,
+ const LineartRenderLine *rl,
+ const double *override_camera_loc,
+ const bool override_cam_is_persp,
+ const bool allow_overlapping_edges,
+ const double vp[4][4],
+ const double *camera_dir,
+ const float cam_shift_x,
+ const float cam_shift_y,
+ double *from,
+ double *to);
+
+static void lineart_add_line_to_list(LineartRenderBuffer *rb, LineartRenderLine *rl);
+
+static void lineart_line_discard_segment(LineartRenderBuffer *rb, LineartRenderLineSegment *rls)
+{
+ BLI_spin_lock(&rb->lock_cuts);
+
+ memset(rls, 0, sizeof(LineartRenderLineSegment));
+
+ /* Storing the node for potentially reuse the memory for new segment data. Line Art data is not
+ * freed after all calulations are done. */
+ BLI_addtail(&rb->wasted_cuts, rls);
+
+ BLI_spin_unlock(&rb->lock_cuts);
+}
+
+static LineartRenderLineSegment *lineart_line_give_segment(LineartRenderBuffer *rb)
+{
+ BLI_spin_lock(&rb->lock_cuts);
+
+ /* See if there is any already allocated memory we can reuse. */
+ 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;
+ }
+ BLI_spin_unlock(&rb->lock_cuts);
+
+ /* Otherwise allocate some new memory. */
+ return (LineartRenderLineSegment *)lineart_mem_aquire_thread(&rb->render_data_pool,
+ sizeof(LineartRenderLineSegment));
+}
+
+/* Cuts the line in image space and mark occlusion level for each segment. */
+static void lineart_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 for some reason the occlusion function may give a result that has zero length, or reversed
+ * in direction, or NAN, so we take care of them here. */
+ if (LRT_DOUBLE_CLOSE_ENOUGH(start, end)) {
+ return;
+ }
+ if (LRT_DOUBLE_CLOSE_ENOUGH(start, 1) || LRT_DOUBLE_CLOSE_ENOUGH(end, 0)) {
+ return;
+ }
+ if (UNLIKELY(start != start)) {
+ start = 0;
+ }
+ if (UNLIKELY(end != end)) {
+ end = 0;
+ }
+
+ if (start > end) {
+ double t = start;
+ start = end;
+ end = t;
+ }
+
+ /* Begin looking for starting position of the segment. */
+ /* Not using a list iteration macro because of it more clear when using for loops to iterate
+ * through the segments. */
+ for (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) {
+ /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle
+ * strip). */
+ if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, end)) {
+ end_segment = rls;
+ ns2 = end_segment;
+ break;
+ }
+ /* This check is to prevent rls->at == 1.0 (where we don't need to cut because we are at the
+ * end point). */
+ if (!rls->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ end_segment = rls;
+ ns2 = end_segment;
+ untouched = 1;
+ break;
+ }
+ /* When an actual cut is needed in the line. */
+ if (rls->at > end) {
+ end_segment = rls;
+ ns2 = lineart_line_give_segment(rb);
+ break;
+ }
+ }
+
+ /* When an actual cut is needed in the line. */
+ if (ns == NULL) {
+ ns = lineart_line_give_segment(rb);
+ }
+ if (ns2 == NULL) {
+ if (untouched) {
+ ns2 = ns;
+ end_segment = ns2;
+ }
+ else {
+ ns2 = lineart_line_give_segment(rb);
+ }
+ }
+
+ /* Insert cutting points. */
+ 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;
+ }
+
+ /* Register 1 level of occlusion. */
+ for (rls = ns; rls && rls != ns2; rls = rls->next) {
+ rls->occlusion++;
+ rls->transparency_mask |= transparency_mask;
+ }
+
+ /* Reduce adjacent cutting points of the same level, which saves memory. */
+ 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);
+ /* This put the node back to the render buffer, if more cut happens, these unused nodes get
+ * picked first. */
+ lineart_line_discard_segment(rb, rls);
+ continue;
+ }
+
+ min_occ = MIN2(min_occ, rls->occlusion);
+
+ prev_rls = rls;
+ }
+ rl->min_occ = min_occ;
+}
+
+/* To see if given line is connected to an adjacent intersection line. */
+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 = lineart_line_first_bounding_area(rb, rl);
+ LineartBoundingArea *nba = ba;
+ LineartRenderTriangleThread *rt;
+
+ /* These values are used for marching along the line. */
+ 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 we are already testing the line in this thread, then don't do it. */
+ 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_image_space_occlusion(&rb->lock_task,
+ (void *)rt,
+ rl,
+ rb->camera_pos,
+ rb->cam_is_persp,
+ rb->allow_overlapping_edges,
+ rb->view_projection,
+ rb->view_vector,
+ rb->shift_x,
+ rb->shift_y,
+ &l,
+ &r)) {
+ lineart_line_cut(rb, rl, l, r, rt->base.transparency_mask);
+ if (rl->min_occ > rb->max_occlusion_level) {
+ /* No need to caluclate any longer on this line because no level more than set value is
+ * going to show up in the rendered result. */
+ return;
+ }
+ }
+ }
+ /* Marching along rl->l to rl->r, searching each possible bounding areas it may touch. */
+ nba = lineart_bounding_area_next(nba, rl, x, y, k, positive_x, positive_y, &x, &y);
+ }
+}
+
+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;
+}
+
+static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartRenderTaskInfo *rti)
+{
+ LineartRenderBuffer *rb = rti->rb;
+ 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);
+ }
+
+ for (lip = (void *)rti->crease; lip && lip != rti->crease_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+
+ for (lip = (void *)rti->intersection; lip && lip != rti->intersection_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+
+ for (lip = (void *)rti->material; lip && lip != rti->material_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+
+ for (lip = (void *)rti->edge_mark; lip && lip != rti->edge_mark_end; lip = lip->next) {
+ lineart_occlusion_single_line(rb, lip, rti->thread_id);
+ }
+ }
+}
+
+/* All internal functions starting with lineart_main_ is called inside
+ * MOD_lineart_compute_feature_lines function.
+ * This function handles all occlusion calculation. */
+static void lineart_main_occlusion_begin(LineartRenderBuffer *rb)
+{
+ int thread_count = rb->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;
+ rti[i].rb = rb;
+ 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);
+}
+
+/* Test if v lies with in the triangle formed by v0, v1, and v2. Returns false when v is exactly on
+ * the edge.
+ * For v to be inside the triangle, it needs to be at the same side of v0->v1, v1->v2, and
+ * v2->v0, where the "side" is determined by checking the sign of cross(v1-v0, v1-v) and so on.
+ */
+static bool lineart_point_inside_triangle(const double v[2],
+ const double v0[2],
+ const double v1[2],
+ const 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 false;
+ }
+
+ c = cl;
+
+ cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]);
+ if (c * cl <= 0) {
+ return false;
+ }
+
+ c = cl;
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ if (c * cl <= 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static int lineart_point_on_line_segment(double v[2], double v0[2], double v1[2])
+{
+ /* c1!=c2 by default. */
+ double c1 = 1, 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;
+}
+
+/* Same algorithm as lineart_point_inside_triangle(), but returns differently:
+ * 0-outside 1-on the edge 2-inside. */
+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_line_segment(v, v0, v1) || lineart_point_on_line_segment(v, v1, v2) ||
+ lineart_point_on_line_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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ if (r == 0) {
+ return 1;
+ }
+
+ return 2;
+}
+
+/* Similar with lineart_point_inside_triangle, but in 3d.
+ * Returns false when not co-plannar. */
+static bool lineart_point_inside_triangle3d(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 false;
+ }
+
+ 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 false;
+ }
+
+ 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 false;
+ }
+
+ return true;
+}
+
+/* The following lineart_memory_get_XXX_space functions are for allocating new memory for some
+ * modified geometries in the culling stage. */
+static LineartRenderElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb)
+{
+ LineartRenderElementLinkNode *reln;
+
+ /* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles
+ * are relatively small. */
+ LineartRenderTriangle *render_triangles = lineart_mem_aquire(&rb->render_data_pool,
+ 64 * rb->triangle_size);
+
+ 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)
+{
+ /* Just re-assign normal and set cull flag. */
+ 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));
+}
+
+/* Does near-plane cut on 1 triangle only. When cutting with far-plane, the camera vectors gets
+ * reversed by the caller so don't need to implement one in a different direction. */
+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 definition of rt->intersecting_verts and the usage in
+ * lineart_geometry_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];
+ /* Init rl to the last rl entry. */
+ 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: /* Triangle is visible. 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];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = rt->v[2];
+
+ 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];
+ rt1->v[1] = rt->v[1];
+ rt1->v[2] = &rv[1];
+
+ lineart_triangle_post(rt1, rt);
+
+ v_count += 2;
+ t_count += 1;
+ }
+ break;
+ case 1:
+ /* One point 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 with near- or far-plane. Setting clip_far = true for cutting with
+ * far-plane. For triangles that's crossing the plane, it will generate new 1 or 2 triangles with
+ * new topology that represents the trimmed triangle. (which then became a triangle or a square
+ * formed by two triangles)
+ */
+static void lineart_main_cull_triangles(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
+}
+
+/* Adjacent data is only used during the initial stages of computing. So we can free it using this
+ * function when it is not needed anymore. */
+static void lineart_main_free_adjacent_data(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++) {
+ /* See definition of rt->intersecting_verts and the usage in
+ * lineart_geometry_object_load() for detailes. */
+ rt->intersecting_verts = NULL;
+ rt = (LineartRenderTriangle *)(((unsigned char *)rt) + rb->triangle_size);
+ }
+ }
+}
+
+static void lineart_main_perspective_division(LineartRenderBuffer *rb)
+{
+ LineartRenderVert *rv;
+ int i;
+
+ 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 (Normalized Device Coordinates)
+ * at the moment.
+ * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed in
+ * the future, the line below correctly transforms it to view space coordinates. */
+ /* 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;
+ }
+ }
+}
+
+/* Transform a single vert to it's viewing position. */
+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);
+}
+
+/* Because we have a variable size for LineartRenderTriangle, we need an access helper. See
+ * LineartRenderTriangleThread for more info. */
+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_identify_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;
+
+ /* The mesh should already be triangulated now, so we can assume each face is a triangle. */
+ 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;
+ }
+
+ 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 we can not get any mesh geometry data from the object */
+ if (!use_mesh) {
+ return;
+ }
+
+ /* First we need to prepare the matrix used for transforming this specific object. */
+ 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) {
+ /* Do not use edit_mesh directly because we will modify it, so create a copy. */
+ 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_FIXED, 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;
+ }
+
+ /* Only allocate memory for verts and tris as we don't know how many lines we will generate
+ * yet. */
+ 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;
+ }
+
+ /* FIXME Yiming: Hack for getting clean 3D text, the seam that extruded text object creates
+ * erroneous detection on creases. Future configuration should allow options. */
+ 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;
+ }
+ /* Register a global index increment. See lineart_triangle_share_edge() and
+ * lineart_main_load_geometries() for detailes. It's okay that global_vindex might eventually
+ * overflow, in such large scene it's virtually impossible for two vertex of the same numeric
+ * index to come close together. */
+ (*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)];
+
+ /* Transparency bit assignment. */
+ 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_identify_feature_line(
+ rb, e, ort, orv, use_crease, ob->type == OB_FONT, CanFindFreestyle, bm);
+ if (eflag) {
+ /* Only allocate for feature lines (instead of all lines) to save memory. */
+ allocate_rl++;
+ }
+ /* Here we just use bm's flag for when loading actual lines, then we don't need to call
+ * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always
+ * set the flag, so hflag stays 0 for lines that are not feature lines. */
+ e->head.hflag = eflag;
+ }
+
+ 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);
+
+ /* Not a feature line, so we skip. */
+ 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
+}
+
+/* See if this object in such collection is used for generating line art,
+ * Disabling a collection for line art will diable all objects inside. */
+static int lineart_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) {
+ switch (c->lineart_usage) {
+ case COLLECTION_LRT_OCCLUSION_ONLY:
+ return OBJECT_LRT_OCCLUSION_ONLY;
+ case COLLECTION_LRT_EXCLUDE:
+ return OBJECT_LRT_EXCLUDE;
+ case COLLECTION_LRT_INTERSECTION_ONLY:
+ return OBJECT_LRT_INTERSECTION_ONLY;
+ case COLLECTION_LRT_NO_INTERSECTION:
+ return OBJECT_LRT_NO_INTERSECTION;
+ }
+ return OBJECT_LRT_INHERENT;
+ }
+ return ob->lineart.usage;
+ }
+ return OBJECT_LRT_INHERENT;
+ }
+
+ LISTBASE_FOREACH (CollectionChild *, cc, &c->children) {
+ int result = lineart_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,
+ bool allow_duplicates)
+{
+ double proj[4][4], view[4][4], result[4][4];
+ float inv[4][4];
+
+ Camera *cam = camera->data;
+ float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
+ 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);
+
+ 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;
+
+ /* Instance duplicated & particles. */
+ if (allow_duplicates) {
+ flags |= DEG_ITER_OBJECT_FLAG_DUPLI;
+ }
+
+ /* This is to serialize vertex index in the whole scene, so lineart_triangle_share_edge() can
+ * work properly from the lack of triangle adjacent info. */
+ int global_i = 0;
+
+ DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) {
+ int usage = lineart_usage_check(scene->master_collection, ob);
+
+ lineart_geometry_object_load(depsgraph, ob, view, proj, rb, usage, &global_i);
+ }
+ DEG_OBJECT_ITER_END;
+}
+
+/* Returns the two other verts of the triangle given a vertex. Returns false if the given vertex
+ * doesn't belong to this triangle. */
+static bool lineart_triangle_get_other_verts(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;
+ }
+ if (rt->v[1] == rv) {
+ *l = rt->v[2];
+ *r = rt->v[0];
+ return true;
+ }
+ if (rt->v[2] == rv) {
+ *l = rt->v[0];
+ *r = rt->v[1];
+ return true;
+ }
+ return false;
+}
+
+static bool lineart_edge_from_triangle(const LineartRenderTriangle *rt,
+ const LineartRenderLine *rl,
+ bool allow_overlapping_edges)
+{
+ /* Normally we just determine from the pointer address. */
+ if (rl->tl == rt || rl->tr == rt) {
+ return true;
+ }
+ /* If allows overlapping, then we compare the vertex coordinates one by one to determine if one
+ * edge is from specific triangle. This is slower but can handle edge split cases very well. */
+ if (allow_overlapping_edges) {
+#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 true;
+ }
+#undef LRT_TRI_SAME_POINT
+ }
+ return false;
+}
+
+/* Sorting three intersection points from min to max,
+ * the order for each intersection is set in lst[0] to lst[2].*/
+#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 returns true, then from/to will carry the occludded segments
+ * in ratio from rl->l to rl->r. The line is later cut with these two values.
+ */
+static bool lineart_triangle_line_image_space_occlusion(SpinLock *UNUSED(spl),
+ const LineartRenderTriangle *rt,
+ const LineartRenderLine *rl,
+ const double *override_camera_loc,
+ const bool override_cam_is_persp,
+ const bool allow_overlapping_edges,
+ const double vp[4][4],
+ const double *camera_dir,
+ const 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;
+
+ /* Overlapping not possible, 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 false;
+ }
+
+ /* If the the line is one of the edge in the triangle, then it's not occludded. */
+ if (lineart_edge_from_triangle(rt, rl, allow_overlapping_edges)) {
+ return false;
+ }
+
+ /* Check 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]);
+
+ /* Sort the intersection distance. */
+ 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_camera_loc);
+ }
+ else {
+ copy_v4_v4_db(vd4, override_camera_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 false;
+ }
+
+ 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; /* Intersection point is not inside triangle. */
+ }
+ }
+
+ st_l = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2);
+ st_r = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2);
+
+ /* Determine the cut position. */
+
+ 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;
+ }
+
+ /* Transform the cut from geometry space to image space. */
+ 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. here the cut is in image space. */
+ 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]);
+ }
+
+ /* Determine the pair of edges that the line has crossed. */
+
+ 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;
+
+ /* Determine the start and end point of image space cut on a line. */
+ if (LF <= 0 && RF <= 0 && (dot_l || dot_r)) {
+ *from = MAX2(0, is[LCross]);
+ *to = MIN2(1, is[RCross]);
+ if (*from >= *to) {
+ return false;
+ }
+ return true;
+ }
+ if (LF >= 0 && RF <= 0 && (dot_l || dot_r)) {
+ *from = MAX2(cut, is[LCross]);
+ *to = MIN2(1, is[RCross]);
+ if (*from >= *to) {
+ return false;
+ }
+ return true;
+ }
+ if (LF <= 0 && RF >= 0 && (dot_l || dot_r)) {
+ *from = MAX2(0, is[LCross]);
+ *to = MIN2(cut, is[RCross]);
+ if (*from >= *to) {
+ return false;
+ }
+ return true;
+ }
+
+ /* Unlikely, but here's the default failed value if anything fall through. */
+ return false;
+}
+
+#undef INTERSECT_SORT_MIN_TO_MAX_3
+#undef INTERSECT_JUST_GREATER
+#undef INTERSECT_JUST_SMALLER
+
+/* At this stage of the computation we don't have triangle adjacent info anymore, so we can only
+ * compare the global vert index. */
+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;
+}
+
+/* To save time and prevent overlapping lines when computing intersection lines. */
+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;
+}
+
+/* This tests a triangle against a virtual line represented by v1---v2. The vertices returned after
+ * repeated calls to this function is then used to create a triangle/triangle intersection line. */
+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));
+
+ /* Due to precision issue, we might end up with the same point as the one we already detected. */
+ if (last && LRT_DOUBLE_CLOSE_ENOUGH(last->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_triangle3d(
+ gloc, testing->v[0]->gloc, testing->v[1]->gloc, testing->v[2]->gloc))) {
+ return NULL;
+ }
+
+ /* This is an intersection vert, the size is bigger than LineartRenderVert,
+ * allocated separately. */
+ result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderVertIntersection));
+
+ /* Indicate the data structure difference. */
+ 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;
+}
+
+/* Test if two triangles intersect. Generates one intersection line if the check succeeds */
+static LineartRenderLine *lineart_triangle_intersect(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) {
+ /* If triangles have sharing points like (abc) and (acd), then we only need to detect bc
+ * against acd or cd against abc.*/
+
+ LineartRenderVert *new_share;
+ lineart_triangle_get_other_verts(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_triangle_get_other_verts(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 {
+ /* If not sharing any points, then we need to try all the possibilities. */
+
+ 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;
+ }
+ }
+
+ /* The intersection line has been generated only in geometry space, so we need to transform them
+ * as well. */
+ 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;
+
+ 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 * LRT_BA_ROWS + col], result);
+ }
+ }
+ }
+
+ rb->intersection_count++;
+
+ return result;
+}
+
+static void lineart_triangle_intersect_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 this is not the smallest subdiv bounding area.*/
+ if (ba->child) {
+ lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[0]);
+ lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[1]);
+ lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[2]);
+ lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[3]);
+ return;
+ }
+
+ /* If this _is_ the smallest subdiv bounding area, then do the intersections there. */
+ for (lip = ba->linked_triangles.first; lip; lip = next_lip) {
+ next_lip = lip->next;
+ testing_triangle = lip->data;
+ rtt = (LineartRenderTriangleThread *)testing_triangle;
+
+ 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;
+
+ /* Bounding box not overlapping, not potential of intersecting. */
+ 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;
+ }
+
+ /* If we do need to compute intersection, then finally do it. */
+ lineart_triangle_intersect(rb, rt, testing_triangle);
+ }
+}
+
+/* The calculated view vector will point towards the far-plane from the camera position. */
+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];
+
+ copy_m4_m4(obmat_no_scale, rb->cam_obmat);
+
+ 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);
+}
+
+static void lineart_destroy_render_data(LineartRenderBuffer *rb)
+{
+ 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 MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd)
+{
+ LineartRenderBuffer *rb = lmd->render_buffer;
+
+ lineart_destroy_render_data(rb);
+
+ if (rb) {
+ MEM_freeN(rb);
+ lmd->render_buffer = NULL;
+ }
+
+ if (G.debug_value == 4000) {
+ printf("LRT: Destroyed render data.\n");
+ }
+}
+
+static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
+ LineartGpencilModifierData *lmd)
+{
+ LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer");
+
+ lmd->render_buffer = rb;
+
+ if (!scene || !scene->camera) {
+ return NULL;
+ }
+ Camera *c = scene->camera->data;
+ double clipping_offset = 0;
+
+ if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) {
+ /* This way the clipped lines are "stablely visible" by prevents depth buffer artefacts. */
+ 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 - lmd->crease_threshold);
+ rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
+ rb->chaining_image_threshold = lmd->chaining_image_threshold;
+ rb->chaining_geometry_threshold = lmd->chaining_geometry_threshold;
+
+ rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0;
+ rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0;
+ rb->allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0;
+ rb->remove_doubles = (lmd->calculation_flags & LRT_REMOVE_DOUBLES) != 0;
+
+ /* See lineart_edge_from_triangle() for how this option may impact performance. */
+ rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0;
+
+ rb->use_contour = (lmd->line_types & LRT_EDGE_FLAG_CONTOUR) != 0;
+ rb->use_crease = (lmd->line_types & LRT_EDGE_FLAG_CREASE) != 0;
+ rb->use_material = (lmd->line_types & LRT_EDGE_FLAG_MATERIAL) != 0;
+ rb->use_edge_marks = (lmd->line_types & LRT_EDGE_FLAG_EDGE_MARK) != 0;
+ rb->use_intersections = (lmd->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);
+
+ return rb;
+}
+
+static int lineart_triangle_size_get(const Scene *scene, LineartRenderBuffer *rb)
+{
+ if (rb->thread_count == 0) {
+ rb->thread_count = BKE_render_num_threads(&scene->r);
+ }
+ return sizeof(LineartRenderTriangle) + (sizeof(LineartRenderLine *) * (rb->thread_count));
+}
+
+static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb)
+{
+ /* Initial tile split is defined as 4 (subdivided as 4*4), increasing the value allows the
+ * algorithm to build the acceleration structure for bigger scenes a little faster but not as
+ * efficient at handling medium to small scenes. */
+ int sp_w = LRT_BA_ROWS;
+ int sp_h = LRT_BA_ROWS;
+ int row, col;
+ LineartBoundingArea *ba;
+
+ /* Because NDC (Normalized Device Coordinates) range is (-1,1),
+ * so the span for each initial tile is double of that in the (0,1) range. */
+ 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);
+
+ /* Initialize tiles. */
+ for (row = 0; row < sp_h; row++) {
+ for (col = 0; col < sp_w; col++) {
+ ba = &rb->initial_bounding_areas[row * LRT_BA_ROWS + col];
+
+ /* Set the four direction limits. */
+ 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;
+
+ /* Link adjacent ones. */
+ if (row) {
+ lineart_list_append_pointer_pool(
+ &ba->up,
+ &rb->render_data_pool,
+ &rb->initial_bounding_areas[(row - 1) * LRT_BA_ROWS + col]);
+ }
+ if (col) {
+ lineart_list_append_pointer_pool(&ba->lp,
+ &rb->render_data_pool,
+ &rb->initial_bounding_areas[row * LRT_BA_ROWS + col - 1]);
+ }
+ if (row != sp_h - 1) {
+ lineart_list_append_pointer_pool(
+ &ba->bp,
+ &rb->render_data_pool,
+ &rb->initial_bounding_areas[(row + 1) * LRT_BA_ROWS + col]);
+ }
+ if (col != sp_w - 1) {
+ lineart_list_append_pointer_pool(&ba->rp,
+ &rb->render_data_pool,
+ &rb->initial_bounding_areas[row * LRT_BA_ROWS + col + 1]);
+ }
+ }
+ }
+}
+
+/* Re-link adjacent tiles after one gets subdivided. */
+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's 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);
+}
+
+/* Subdivide a tile after one tile contains too many triangles. */
+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 bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb),
+ const double l[2],
+ const 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 false;
+ }
+
+ 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 true;
+ }
+ c = c1;
+
+ c1 = vx * (converted[3] - l[1]) - vy * (converted[0] - l[0]);
+ if (c1 * c <= 0) {
+ return true;
+ }
+ c = c1;
+
+ c1 = vx * (converted[3] - l[1]) - vy * (converted[1] - l[0]);
+ if (c1 * c <= 0) {
+ return true;
+ }
+ c = c1;
+
+ return false;
+}
+
+static bool lineart_bounding_area_triangle_intersect(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 true;
+ }
+
+ if (lineart_point_inside_triangle(p1, FBC1, FBC2, FBC3) ||
+ lineart_point_inside_triangle(p2, FBC1, FBC2, FBC3) ||
+ lineart_point_inside_triangle(p3, FBC1, FBC2, FBC3) ||
+ lineart_point_inside_triangle(p4, FBC1, FBC2, FBC3)) {
+ return true;
+ }
+
+ if ((lineart_bounding_area_line_intersect(fb, FBC1, FBC2, ba)) ||
+ (lineart_bounding_area_line_intersect(fb, FBC2, FBC3, ba)) ||
+ (lineart_bounding_area_line_intersect(fb, FBC3, FBC1, ba))) {
+ return true;
+ }
+
+ return false;
+}
+
+/* 1) Link triangles with bounding areas for later occlusion test.
+ * 2) Test triangles with existing(added previously) triangles for intersection lines. */
+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_intersect(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_intersect_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_intersect(
+ 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_intersect(
+ 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_intersect(
+ 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_intersect(
+ rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[3])) {
+ lineart_bounding_area_link_line(rb, &root_ba->child[3], rl);
+ }
+ }
+}
+
+/* Link lines to their respective bounding areas. */
+static void lineart_main_link_lines(LineartRenderBuffer *rb)
+{
+ LRT_ITER_ALL_LINES_BEGIN
+ {
+ int r1, r2, c1, c2, row, col;
+ if (lineart_get_line_bounding_areas(rb, rl, &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 * LRT_BA_ROWS + col], rl);
+ }
+ }
+ }
+ }
+ LRT_ITER_ALL_LINES_END
+}
+
+static bool 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 false;
+ }
+
+ 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 false;
+ }
+
+ (*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 true;
+}
+
+static bool 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 false;
+ }
+
+ if (rl->l->fbcoord[0] != rl->l->fbcoord[0] || rl->r->fbcoord[0] != rl->r->fbcoord[0]) {
+ return false;
+ }
+
+ 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 false;
+ }
+
+ (*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 true;
+}
+
+/* This only gets initial "biggest" tile. */
+LineartBoundingArea *MOD_lineart_get_parent_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 * LRT_BA_ROWS + col];
+}
+
+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 * LRT_BA_ROWS + 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;
+}
+
+/* Wrapper for more convenience. */
+LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y)
+{
+ LineartBoundingArea *ba;
+ if ((ba = MOD_lineart_get_parent_bounding_area(rb, x, y)) != NULL) {
+ return lineart_get_bounding_area(rb, x, y);
+ }
+ return NULL;
+}
+
+/* Sequentially add triangles into render buffer. This also does intersection along the way. */
+static void lineart_main_add_triangles(LineartRenderBuffer *rb)
+{
+ LineartRenderTriangle *rt;
+ int i, lim;
+ int x1, x2, y1, y2;
+ int r, co;
+
+ 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 * LRT_BA_ROWS + co],
+ rt,
+ 0,
+ 1,
+ 0,
+ (!(rt->flags & LRT_TRIANGLE_NO_INTERSECTION)));
+ }
+ }
+ } /* Else throw away. */
+ rt = (void *)(((unsigned char *)rt) + rb->triangle_size);
+ }
+ }
+}
+
+/* This function gets the tile for the point rl->l, and later use lineart_bounding_area_next() to
+ * get next along the way. */
+static LineartBoundingArea *lineart_line_first_bounding_area(LineartRenderBuffer *rb,
+ LineartRenderLine *rl)
+{
+ 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]);
+ }
+
+ 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]);
+}
+
+/* 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 {
+ /* egment has no length. */
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/* This is the entry point of all line art calculations. */
+int MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd)
+{
+ LineartRenderBuffer *rb;
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ int intersections_only = 0; /* Not used right now, but preserve for future. */
+
+ if (!scene->camera) {
+ return OPERATOR_CANCELLED;
+ }
+
+ rb = lineart_create_render_buffer(scene, lmd);
+
+ /* Triangle thread testing data size varies depending on the thread count.
+ * See definition of LineartRenderTriangleThread for details. */
+ rb->triangle_size = lineart_triangle_size_get(scene, rb);
+
+ /* This is used to limit calculation to a certain level to save time, lines who have higher
+ * occlusion levels will get ignored. */
+ rb->max_occlusion_level = MAX2(lmd->level_start, lmd->level_end);
+
+ /* Get view vector before loading geometries, because we detect feature lines there. */
+ lineart_main_get_view_vector(rb);
+ lineart_main_load_geometries(
+ depsgraph, scene, scene->camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS);
+
+ if (!rb->vertex_buffer_pointers.first) {
+ /* No geometry loaded, return early. */
+ return OPERATOR_FINISHED;
+ }
+
+ /* Initialize the bounding box acceleration structure, it's a lot like BVH in 3D. */
+ lineart_main_bounding_area_make_initial(rb);
+
+ /* We need to get cut into triangles that are crossing near/far plans, only this way can we get
+ * correct coordinates of those clipped lines. Done in two steps,
+ * setting clip_far==false for near plane. */
+ lineart_main_cull_triangles(rb, false);
+ /* clip_far==true for far plane. */
+ lineart_main_cull_triangles(rb, true);
+
+ /* At this point triangle adjacent info pointers is no longer needed, free them. */
+ lineart_main_free_adjacent_data(rb);
+
+ /* Do the perspective division after clipping is done. */
+ lineart_main_perspective_division(rb);
+
+ /* Triangle intersections are done here during sequential adding of them. Only after this,
+ * triangles and lines are all linked with acceleration structure, and the 2D occlusion stage can
+ * do its job. */
+ lineart_main_add_triangles(rb);
+
+ /* Link lines to acceleration structure, this can only be done after perspective division, if we
+ * do it after triangles being added, the acceleration structure has already been subdivided,
+ * this way we do less list manipulations. */
+ lineart_main_link_lines(rb);
+
+ /* "intersection_only" is preserved for being called in a standalone fashion.
+ * If so the data will already be available at the stage. Otherwise we do the occlusion and
+ * chaining etc.*/
+
+ if (!intersections_only) {
+
+ /* Occlusion is work-and-wait. This call will not return before work is completed. */
+ lineart_main_occlusion_begin(rb);
+
+ /* Chaining is all single threaded. See lineart_chain.c
+ * In this particular call, only lines that are geometrically connected (share the _exact_ same
+ * end point) will be chained together. */
+ MOD_lineart_chain_feature_lines(rb);
+
+ /* We are unable to take care of occlusion if we only connect end points, so here we do a spit,
+ * where the splitting point could be any cut in rl->segments. */
+ MOD_lineart_chain_split_for_fixed_occlusion(rb);
+
+ /* Then we connect chains based on the _proximity_ of their end points in geometry or image
+ * space, here's the place threashold value gets involved. */
+
+ /* If both chaining thresholds are zero, then we allow at least image space chaining to do a
+ * little bit of work so we don't end up in fragmented strokes. */
+ float *t_image = &lmd->chaining_image_threshold;
+ float *t_geom = &lmd->chaining_geometry_threshold;
+ if (*t_image < FLT_EPSILON && *t_geom < FLT_EPSILON) {
+ *t_geom = 0.0f;
+ *t_image = 0.001f;
+ }
+
+ /* do_geometry_space = true. */
+ MOD_lineart_chain_connect(rb, true);
+
+ /* After chaining, we need to clear flags so we can do another round in image space. */
+ MOD_lineart_chain_clear_picked_flag(rb);
+
+ /* do_geometry_space = false (it's image_space). */
+ MOD_lineart_chain_connect(rb, false);
+
+ /* Clear again so we don't confuse GPencil generation calls. */
+ MOD_lineart_chain_clear_picked_flag(rb);
+
+ /* This configuration ensures there won't be accidental lost of short unchained segments. */
+ MOD_lineart_chain_discard_short(rb, MIN3(*t_image, *t_geom, 0.001f) - FLT_EPSILON);
+
+ if (rb->angle_splitting_threshold > FLT_EPSILON) {
+ MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold);
+ }
+ }
+
+ if (G.debug_value == 4000) {
+ lineart_count_and_print_render_buffer_memory(rb);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+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;
+}
+
+static void lineart_gpencil_generate(LineartRenderBuffer *rb,
+ 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)
+{
+ if (rb == NULL) {
+ if (G.debug_value == 4000) {
+ printf("NULL Lineart rb!\n");
+ }
+ return;
+ }
+
+ 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;
+ }
+
+ /* (!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;
+ }
+ }
+ }
+
+ /* Preserved: If we ever do async generation, this picked flag should be set here. */
+ /* rlc->picked = 1;. */
+
+ int array_idx = 0;
+ int count = MOD_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;
+ 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);
+ }
+}
+
+/* Wrapper for external calls. */
+void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
+ 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);
+ lineart_gpencil_generate(rb,
+ 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);
+}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
new file mode 100644
index 00000000000..99cfa06b2fe
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -0,0 +1,113 @@
+/*
+ * 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
+ */
+
+#pragma once
+
+#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 LineartStaticMemPool;
+struct LineartStaticMemPoolNode;
+struct LineartRenderLine;
+struct LineartRenderBuffer;
+
+void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data);
+void *lineart_list_append_pointer_pool_sized(ListBase *h,
+ struct LineartStaticMemPool *smp,
+ void *data,
+ int size);
+void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p);
+void *list_push_pointer_static_sized(ListBase *h,
+ 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);
+
+struct 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(struct LineartStaticMemPool *smp);
+
+void lineart_prepend_line_direct(struct LineartRenderLine **first, void *node);
+void lineart_prepend_pool(LinkNode **first, struct 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(struct 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_NEXT \
+ 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_ITER_ALL_LINES_END \
+ LRT_ITER_ALL_LINES_NEXT \
+ }
+
+#define LRT_BOUND_AREA_CROSSES(b1, b2) \
+ ((b1)[0] < (b2)[1] && (b1)[1] > (b2)[0] && (b1)[3] < (b2)[2] && (b1)[2] > (b2)[3])
+
+/* Initial bounding area row/column count, setting 4 is the simplest way algorithm could function
+ * efficiently. */
+#define LRT_BA_ROWS 4
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
new file mode 100644
index 00000000000..d5bd232cc9c
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
@@ -0,0 +1,439 @@
+/*
+ * 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 "MEM_guardedalloc.h"
+
+#include "BKE_collection.h"
+#include "BKE_context.h"
+#include "BKE_global.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 "MOD_gpencil_lineart.h"
+#include "MOD_lineart.h"
+
+#include "lineart_intern.h"
+
+static void clear_strokes(Object *ob, GpencilModifierData *md, int frame)
+{
+ if (md->type != eGpencilModifierType_Lineart) {
+ return;
+ }
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ bGPdata *gpd = ob->data;
+
+ bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
+ if (!gpl) {
+ return;
+ }
+ bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, frame);
+
+ if (!gpf) {
+ /* No greasepencil frame found. */
+ return;
+ }
+
+ BKE_gpencil_layer_frame_delete(gpl, gpf);
+}
+
+static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int frame)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ bGPdata *gpd = ob->data;
+
+ bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
+ if (!gpl) {
+ return false;
+ }
+ bool only_use_existing_gp_frames = false;
+ bGPDframe *gpf = (only_use_existing_gp_frames ?
+ BKE_gpencil_layer_frame_find(gpl, frame) :
+ BKE_gpencil_layer_frame_get(gpl, frame, GP_GETFRAME_ADD_NEW));
+
+ if (!gpf) {
+ /* No greasepencil frame created or found. */
+ return false;
+ }
+
+ MOD_lineart_compute_feature_lines(dg, lmd);
+
+ MOD_lineart_gpencil_generate(
+ lmd->render_buffer,
+ 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,
+ 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);
+
+ MOD_lineart_destroy_render_data(lmd);
+
+ return true;
+}
+
+typedef struct LineartBakeJob {
+ wmWindowManager *wm;
+ void *owner;
+ short *stop, *do_update;
+ float *progress;
+
+ /* C or ob must have one != NULL. */
+ bContext *C;
+ LinkNode *objects;
+ Scene *scene;
+ Depsgraph *dg;
+ int frame;
+ int frame_begin;
+ int frame_end;
+ int frame_orig;
+ int frame_increment;
+ bool overwrite_frames;
+} LineartBakeJob;
+
+static bool lineart_gpencil_bake_single_target(LineartBakeJob *bj, Object *ob, int frame)
+{
+ bool touched = false;
+ if (ob->type != OB_GPENCIL || G.is_break) {
+ return false;
+ }
+
+ if (bj->overwrite_frames) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ clear_strokes(ob, md, frame);
+ }
+ }
+
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ if (bake_strokes(ob, bj->dg, md, frame)) {
+ touched = true;
+ }
+ }
+
+ return touched;
+}
+
+static void lineart_gpencil_guard_modifiers(LineartBakeJob *bj)
+{
+ for (LinkNode *l = bj->objects; l; l = l->next) {
+ Object *ob = l->link;
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ if (md->type == eGpencilModifierType_Lineart) {
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ lmd->flags |= LRT_GPENCIL_IS_BAKED;
+ }
+ }
+ }
+}
+
+static void lineart_gpencil_bake_startjob(void *customdata,
+ short *stop,
+ short *do_update,
+ float *progress)
+{
+ LineartBakeJob *bj = (LineartBakeJob *)customdata;
+ bj->stop = stop;
+ bj->do_update = do_update;
+ bj->progress = progress;
+
+ lineart_gpencil_guard_modifiers(bj);
+
+ for (int frame = bj->frame_begin; frame <= bj->frame_end; frame += bj->frame_increment) {
+
+ if (G.is_break) {
+ G.is_break = false;
+ break;
+ }
+
+ BKE_scene_frame_set(bj->scene, frame);
+ BKE_scene_graph_update_for_newframe(bj->dg);
+
+ for (LinkNode *l = bj->objects; l; l = l->next) {
+ Object *ob = l->link;
+ if (lineart_gpencil_bake_single_target(bj, ob, frame)) {
+ DEG_id_tag_update((struct ID *)ob->data, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(bj->C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
+ }
+ }
+
+ /* Update and refresh the progress bar. */
+ *bj->progress = (float)(frame - bj->frame_begin) / (bj->frame_end - bj->frame_begin);
+ *bj->do_update = true;
+ }
+
+ /* This need to be reset manually. */
+ G.is_break = false;
+
+ /* Restore original frame. */
+ BKE_scene_frame_set(bj->scene, bj->frame_orig);
+ BKE_scene_graph_update_for_newframe(bj->dg);
+}
+
+static void lineart_gpencil_bake_endjob(void *customdata)
+{
+ LineartBakeJob *bj = customdata;
+
+ WM_set_locked_interface(CTX_wm_manager(bj->C), false);
+
+ WM_main_add_notifier(NC_SCENE | ND_FRAME, bj->scene);
+
+ for (LinkNode *l = bj->objects; l; l = l->next) {
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, (Object *)l->link);
+ }
+
+ BLI_linklist_free(bj->objects, NULL);
+}
+
+static int lineart_gpencil_bake_common(bContext *C,
+ wmOperator *op,
+ bool bake_all_targets,
+ bool do_background)
+{
+ LineartBakeJob *bj = MEM_callocN(sizeof(LineartBakeJob), "LineartBakeJob");
+
+ if (!bake_all_targets) {
+ Object *ob = CTX_data_active_object(C);
+ if (!ob || ob->type != OB_GPENCIL) {
+ WM_report(RPT_ERROR, "No active object or active object isn't a GPencil object.");
+ return OPERATOR_FINISHED;
+ }
+ BLI_linklist_prepend(&bj->objects, ob);
+ }
+ else {
+ /* CTX_DATA_BEGIN is not available for interating in objects while using the Job system. */
+ CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
+ if (ob->type == OB_GPENCIL) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ if (md->type == eGpencilModifierType_Lineart) {
+ BLI_linklist_prepend(&bj->objects, ob);
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+ }
+ bj->C = C;
+ Scene *scene = CTX_data_scene(C);
+ bj->scene = scene;
+ bj->dg = CTX_data_depsgraph_pointer(C);
+ bj->frame_begin = scene->r.sfra;
+ bj->frame_end = scene->r.efra;
+ bj->frame_orig = scene->r.cfra;
+ bj->frame_increment = scene->r.frame_step;
+ bj->overwrite_frames = true;
+
+ if (do_background) {
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ scene,
+ "Line Art",
+ WM_JOB_PROGRESS,
+ WM_JOB_TYPE_LINEART);
+
+ WM_jobs_customdata_set(wm_job, bj, MEM_freeN);
+ WM_jobs_timer(wm_job, 0.1, NC_GPENCIL | ND_DATA | NA_EDITED, NC_GPENCIL | ND_DATA | NA_EDITED);
+ WM_jobs_callbacks(
+ wm_job, lineart_gpencil_bake_startjob, NULL, NULL, lineart_gpencil_bake_endjob);
+
+ WM_set_locked_interface(CTX_wm_manager(C), true);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ float pseduo_progress;
+ short pseduo_do_update;
+ lineart_gpencil_bake_startjob(bj, NULL, &pseduo_do_update, &pseduo_progress);
+
+ BLI_linklist_free(bj->objects, NULL);
+ MEM_freeN(bj);
+
+ return OPERATOR_FINISHED;
+}
+
+static int lineart_gpencil_bake_strokes_all_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ return lineart_gpencil_bake_common(C, op, true, true);
+}
+static int lineart_gpencil_bake_strokes_all_exec(bContext *C, wmOperator *op)
+{
+ return lineart_gpencil_bake_common(C, op, true, false);
+}
+static int lineart_gpencil_bake_strokes_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ return lineart_gpencil_bake_common(C, op, false, true);
+}
+static int lineart_gpencil_bake_strokes_exec(bContext *C, wmOperator *op)
+{
+ return lineart_gpencil_bake_common(C, op, false, false);
+
+ return OPERATOR_FINISHED;
+}
+static int lineart_gpencil_bake_strokes_commom_modal(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ Scene *scene = (Scene *)op->customdata;
+
+ /* no running blender, remove handler and pass through. */
+ if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LINEART)) {
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+static void lineart_gpencil_clear_strokes_exec_common(Object *ob)
+{
+ if (ob->type != OB_GPENCIL) {
+ return;
+ }
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ if (md->type != eGpencilModifierType_Lineart) {
+ continue;
+ }
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ bGPdata *gpd = ob->data;
+
+ bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
+ if (!gpl) {
+ continue;
+ }
+ BKE_gpencil_free_frames(gpl);
+
+ md->mode |= eGpencilModifierMode_Realtime | eGpencilModifierMode_Render;
+
+ lmd->flags &= (~LRT_GPENCIL_IS_BAKED);
+ }
+ DEG_id_tag_update((struct ID *)ob->data, ID_RECALC_GEOMETRY);
+}
+
+static int lineart_gpencil_clear_strokes_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ lineart_gpencil_clear_strokes_exec_common(ob);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
+
+ return OPERATOR_FINISHED;
+}
+static int lineart_gpencil_clear_strokes_all_exec(bContext *C, wmOperator *op)
+{
+ CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
+ lineart_gpencil_clear_strokes_exec_common(ob);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
+ }
+ CTX_DATA_END;
+
+ BKE_report(op->reports, RPT_INFO, "All line art objects are now cleared.");
+
+ return OPERATOR_FINISHED;
+}
+
+/* Bake all line art modifiers on the current object. */
+void OBJECT_OT_lineart_bake_strokes(wmOperatorType *ot)
+{
+ ot->name = "Bake Line Art";
+ ot->description = "Bake Line Art for current GPencil object";
+ ot->idname = "OBJECT_OT_lineart_bake_strokes";
+
+ ot->invoke = lineart_gpencil_bake_strokes_invoke;
+ ot->exec = lineart_gpencil_bake_strokes_exec;
+ ot->modal = lineart_gpencil_bake_strokes_commom_modal;
+}
+
+/* Bake all lineart objects in the scene. */
+void OBJECT_OT_lineart_bake_strokes_all(wmOperatorType *ot)
+{
+ ot->name = "Bake Line Art (All)";
+ ot->description = "Bake all GPencil objects who has at least one Line Art modifier";
+ ot->idname = "OBJECT_OT_lineart_bake_strokes_all";
+
+ ot->invoke = lineart_gpencil_bake_strokes_all_invoke;
+ ot->exec = lineart_gpencil_bake_strokes_all_exec;
+ ot->modal = lineart_gpencil_bake_strokes_commom_modal;
+}
+
+/* clear all line art modifiers on the current object. */
+void OBJECT_OT_lineart_clear(wmOperatorType *ot)
+{
+ ot->name = "Clear Baked Line Art";
+ ot->description = "Clear all strokes in current GPencil obejct";
+ ot->idname = "OBJECT_OT_lineart_clear";
+
+ ot->exec = lineart_gpencil_clear_strokes_exec;
+}
+
+/* clear all lineart objects in the scene. */
+void OBJECT_OT_lineart_clear_all(wmOperatorType *ot)
+{
+ ot->name = "Clear Baked Line Art (All)";
+ ot->description = "Clear all strokes in all GPencil obejcts who has a Line Art modifier";
+ ot->idname = "OBJECT_OT_lineart_clear_all";
+
+ ot->exec = lineart_gpencil_clear_strokes_all_exec;
+}
+
+void WM_operatortypes_lineart(void)
+{
+ WM_operatortype_append(OBJECT_OT_lineart_bake_strokes);
+ WM_operatortype_append(OBJECT_OT_lineart_bake_strokes_all);
+ WM_operatortype_append(OBJECT_OT_lineart_clear);
+ WM_operatortype_append(OBJECT_OT_lineart_clear_all);
+}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
new file mode 100644
index 00000000000..0d145042e08
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/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 "MEM_guardedalloc.h"
+#include "MOD_lineart.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/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_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index 11d4ee1b2cd..baf2b2414fa 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -283,4 +283,17 @@
.colorband = NULL, \
}
+#define _DNA_DEFAULT_LineartGpencilModifierData \
+ { \
+ .line_types = LRT_EDGE_FLAG_ALL_TYPE, \
+ .thickness = 25, \
+ .opacity = 1.0f, \
+ .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP | LRT_GPENCIL_SOFT_SELECTION, \
+ .crease_threshold = DEG2RAD(140.0f), \
+ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_REMOVE_DOUBLES | LRT_ALLOW_OVERLAPPING_EDGES | LRT_ALLOW_CLIPPING_BOUNDARIES, \
+ .angle_splitting_threshold = DEG2RAD(60.0f), \
+ .chaining_geometry_threshold = 0.001f, \
+ .chaining_image_threshold = 0.001f, \
+ }
+
/* clang-format off */
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index b2d62e0a5f6..a9262bf7ca4 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,78 @@ 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),
+ LRT_GPENCIL_IS_BAKED = (1 << 3),
+} 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;
+
+ /** 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;
+
+ float pre_sample_length;
+
+ /* Ported from SceneLineArt flags. */
+ int calculation_flags;
+
+ /* Additional Switches. */
+ int flags;
+
+ int _pad;
+
+ /* Runtime only. */
+ void *render_buffer;
+
+} LineartGpencilModifierData;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 9d969a29add..8facdca2f9c 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -34,6 +34,7 @@ extern "C" {
struct AnimData;
struct Curve;
struct MDeformVert;
+struct Curve;
#define GP_DEFAULT_PIX_FACTOR 1.0f
#define GP_DEFAULT_GRID_LINES 4
@@ -412,6 +413,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..2eb36cfb9d3
--- /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 eLineartMainFlags {
+ LRT_INTERSECTION_AS_CONTOUR = (1 << 0),
+ LRT_EVERYTHING_AS_CONTOUR = (1 << 1),
+ LRT_ALLOW_DUPLI_OBJECTS = (1 << 2),
+ LRT_ALLOW_OVERLAPPING_EDGES = (1 << 3),
+ LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 4),
+ LRT_REMOVE_DOUBLES = (1 << 5),
+} eLineartMainFlags;
+
+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
+
+#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_space_types.h b/source/blender/makesdna/DNA_space_types.h
index de9b842312c..755010cd8a5 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -228,6 +228,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/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 7aca742a8e6..95272fb7804 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -315,6 +315,7 @@ SDNA_DEFAULT_DECL_STRUCT(TextureGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData);
+SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@@ -539,6 +540,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(ThickGpencilModifierData),
SDNA_DEFAULT_DECL(TimeGpencilModifierData),
SDNA_DEFAULT_DECL(TintGpencilModifierData),
+ SDNA_DEFAULT_DECL(LineartGpencilModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX
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 a9125c78229..ba67cedfdbe 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -456,6 +456,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;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 94cfd8464ae..c04ccf59327 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -443,6 +443,7 @@ set(LIB
bf_editor_undo
)
+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..90e969560b7 100644
--- a/source/blender/makesrna/intern/rna_collection.c
+++ b/source/blender/makesrna/intern/rna_collection.c
@@ -22,6 +22,8 @@
#include "DNA_collection_types.h"
+#include "DNA_lineart_types.h"
+
#include "BLI_utildefines.h"
#include "RNA_define.h"
@@ -517,6 +519,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..b84fcf4cf7c 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:
@@ -311,6 +318,7 @@ 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,281 @@ 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", "Generate 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, "fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR);
+ RNA_def_property_ui_text(prop,
+ "Intersection With Contour",
+ "Treat intersection and contour lines as if they were the same type so "
+ "they can be chained together");
+ RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fuzzy_everything", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_EVERYTHING_AS_CONTOUR);
+ RNA_def_property_ui_text(
+ prop, "All Lines", "Treat all lines as the same line type so they can be chained together");
+ RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "allow_duplication", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_DUPLI_OBJECTS);
+ 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_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "allow_overlapping_edges", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_OVERLAPPING_EDGES);
+ 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_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "allow_clipping_boundaries", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_CLIPPING_BOUNDARIES);
+ 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_GpencilModifier_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_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "angle_splitting_threshold", PROP_FLOAT, PROP_ANGLE);
+ 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_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "remove_doubles", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES);
+ RNA_def_property_ui_text(
+ prop, "Remove Doubles", "Remove doubles when line art is loading geometries");
+ RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "chaining_geometry_threshold", PROP_FLOAT, PROP_DISTANCE);
+ 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_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "chaining_image_threshold", PROP_FLOAT, PROP_DISTANCE);
+ 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_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, modifier_lineart_source_type);
+ RNA_def_property_ui_text(prop, "Source Type", "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_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_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_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_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_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, "is_baked", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_IS_BAKED);
+ RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE);
+ 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_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_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_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_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_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_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_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_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_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_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_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_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 +2662,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..39dd3059f3d 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -127,6 +127,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 *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Material *ma = (Material *)ptr->owner_id;
+ /* Need to tag geometry for line art modifier updates. */
+ DEG_id_tag_update(&ma->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_MATERIAL | ND_SHADING_DRAW, ma);
+}
+
static void rna_Material_draw_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Material *ma = (Material *)ptr->owner_id;
@@ -671,6 +679,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 +908,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..8c12ccceb5d 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_DATA,
+ "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)
@@ -2645,6 +2667,59 @@ 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)
{
StructRNA *srna;
@@ -3414,6 +3489,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 +3514,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_space.c b/source/blender/makesrna/intern/rna_space.c
index 0ddc9a61cad..8377a8fbf3f 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -458,6 +458,7 @@ 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",
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 545371f4540..9f70d05cc8a 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -755,6 +755,7 @@ enum {
WM_JOB_TYPE_FSMENU_BOOKMARK_VALIDATE,
WM_JOB_TYPE_QUADRIFLOW_REMESH,
WM_JOB_TYPE_TRACE_IMAGE,
+ WM_JOB_TYPE_LINEART,
/* add as needed, bake, seq proxy build
* if having hard coded values is a problem */
};