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--CMakeLists.txt4
-rw-r--r--build_files/cmake/config/blender_full.cmake1
-rw-r--r--build_files/cmake/config/blender_lite.cmake1
-rw-r--r--build_files/cmake/config/blender_release.cmake1
m---------release/datafiles/locale0
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/startup/bl_ui/__init__.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_collection.py125
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py32
-rw-r--r--release/scripts/startup/bl_ui/properties_lanpr.py100
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py321
-rw-r--r--release/scripts/startup/bl_ui/properties_world.py4
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py23
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_scene.h5
-rw-r--r--source/blender/blenkernel/CMakeLists.txt4
-rw-r--r--source/blender/blenkernel/intern/collection.c3
-rw-r--r--source/blender/blenkernel/intern/gpencil.c40
-rw-r--r--source/blender/blenkernel/intern/scene.c443
-rw-r--r--source/blender/blenloader/intern/readfile.c57
-rw-r--r--source/blender/blenloader/intern/versioning_280.c15
-rw-r--r--source/blender/blenloader/intern/writefile.c11
-rw-r--r--source/blender/draw/CMakeLists.txt24
-rw-r--r--source/blender/draw/DRW_engine.h5
-rw-r--r--source/blender/draw/engines/lanpr/lanpr_all.h247
-rw-r--r--source/blender/draw/engines/lanpr/lanpr_chain_draw.c211
-rw-r--r--source/blender/draw/engines/lanpr/lanpr_cpu.c520
-rw-r--r--source/blender/draw/engines/lanpr/lanpr_dpix.c558
-rw-r--r--source/blender/draw/engines/lanpr/lanpr_engine.c799
-rw-r--r--source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_frag.glsl6
-rw-r--r--source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_geom.glsl193
-rw-r--r--source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_clip_frag.glsl469
-rw-r--r--source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_passthrough_vert.glsl6
-rw-r--r--source/blender/draw/engines/lanpr/shaders/lanpr_software_chain_geom.glsl293
-rw-r--r--source/blender/draw/engines/lanpr/shaders/lanpr_software_line_chain_geom.glsl185
-rw-r--r--source/blender/draw/engines/lanpr/shaders/lanpr_software_passthrough_vert.glsl21
-rw-r--r--source/blender/draw/intern/DRW_render.h11
-rw-r--r--source/blender/draw/intern/draw_manager.c7
-rw-r--r--source/blender/draw/intern/draw_manager_texture.c24
-rw-r--r--source/blender/editors/CMakeLists.txt5
-rw-r--r--source/blender/editors/include/ED_lanpr.h625
-rw-r--r--source/blender/editors/lanpr/CMakeLists.txt46
-rw-r--r--source/blender/editors/lanpr/lanpr_chain.c759
-rw-r--r--source/blender/editors/lanpr/lanpr_cpu.c4467
-rw-r--r--source/blender/editors/lanpr/lanpr_intern.h74
-rw-r--r--source/blender/editors/lanpr/lanpr_ops.c48
-rw-r--r--source/blender/editors/lanpr/lanpr_util.c191
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt4
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c4
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c2
-rw-r--r--source/blender/editors/render/CMakeLists.txt4
-rw-r--r--source/blender/editors/render/render_update.c8
-rw-r--r--source/blender/editors/space_api/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_api/spacetypes.c8
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c43
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c15
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c5
-rw-r--r--source/blender/editors/util/CMakeLists.txt5
-rw-r--r--source/blender/makesdna/DNA_brush_types.h16
-rw-r--r--source/blender/makesdna/DNA_collection_types.h50
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_lanpr_types.h126
-rw-r--r--source/blender/makesdna/DNA_object_types.h41
-rw-r--r--source/blender/makesdna/DNA_scene_types.h140
-rw-r--r--source/blender/makesdna/DNA_space_types.h2
-rw-r--r--source/blender/makesdna/intern/makesdna.c5
-rw-r--r--source/blender/makesrna/RNA_access.h3
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt11
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_collection.c166
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_lanpr.c192
-rw-r--r--source/blender/makesrna/intern/rna_object.c110
-rw-r--r--source/blender/makesrna/intern/rna_scene.c281
-rw-r--r--source/blender/makesrna/intern/rna_space.c18
-rw-r--r--source/blender/python/intern/CMakeLists.txt4
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c7
81 files changed, 12221 insertions, 60 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a4037dc1e3e..6268f793062 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -322,6 +322,9 @@ mark_as_advanced(WITH_SYSTEM_GLOG)
# Freestyle
option(WITH_FREESTYLE "Enable Freestyle (advanced edges rendering)" ON)
+# LANPR
+option(WITH_LANPR "Enable LANPR (more advanced edges rendering)" ON)
+
# New object types
option(WITH_NEW_OBJECT_TYPES "Enable new hair and pointcloud objects (use for development only, don't save in files)" OFF)
mark_as_advanced(WITH_NEW_OBJECT_TYPES)
@@ -1726,6 +1729,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_INPUT_NDOF)
info_cfg_option(WITH_CYCLES)
info_cfg_option(WITH_FREESTYLE)
+ info_cfg_option(WITH_LANPR)
info_cfg_option(WITH_OPENCOLORIO)
info_cfg_option(WITH_XR_OPENXR)
info_cfg_option(WITH_OPENIMAGEDENOISE)
diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake
index a997d7c0e68..56b700d75cd 100644
--- a/build_files/cmake/config/blender_full.cmake
+++ b/build_files/cmake/config/blender_full.cmake
@@ -18,6 +18,7 @@ set(WITH_LIBMV ON CACHE BOOL "" FORCE)
set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE)
set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE)
set(WITH_FREESTYLE ON CACHE BOOL "" FORCE)
+set(WITH_LANPR ON CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE)
set(WITH_IK_ITASC ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake
index f3a6d4608fe..32c187f5f72 100644
--- a/build_files/cmake/config/blender_lite.cmake
+++ b/build_files/cmake/config/blender_lite.cmake
@@ -23,6 +23,7 @@ set(WITH_LIBMV OFF CACHE BOOL "" FORCE)
set(WITH_LLVM OFF CACHE BOOL "" FORCE)
set(WITH_COMPOSITOR OFF CACHE BOOL "" FORCE)
set(WITH_FREESTYLE OFF CACHE BOOL "" FORCE)
+set(WITH_LANPR OFF CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER OFF CACHE BOOL "" FORCE)
set(WITH_IK_ITASC OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake
index 01a59e451aa..86707a62a1b 100644
--- a/build_files/cmake/config/blender_release.cmake
+++ b/build_files/cmake/config/blender_release.cmake
@@ -19,6 +19,7 @@ set(WITH_LIBMV ON CACHE BOOL "" FORCE)
set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE)
set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE)
set(WITH_FREESTYLE ON CACHE BOOL "" FORCE)
+set(WITH_LANPR ON CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE)
set(WITH_IK_ITASC ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE)
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 0fd21a7cc382066d184fda8153f925bb825af2c
+Subproject 8a05b618f031582c006c6f62b9e60619ab3eef8
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 9a9832d5d7fe61a446516f2e2722f8356bd7e70
+Subproject 67f1fbca1482d9d9362a4001332e785c3fd5d23
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 9468c406fb554e32ff47b62bfef356b3908ec65
+Subproject ef6ef414d22c2578fad99327743b925ab640a99
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index 7d3ecceca41..36df0256c3d 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
#
@@ -103,6 +104,9 @@ import bpy
if bpy.app.build_options.freestyle:
_modules.append("properties_freestyle")
+if bpy.app.build_options.lanpr:
+ _modules.append("properties_lanpr")
+
__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..3713b394b62
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_collection.py
@@ -0,0 +1,125 @@
+# ##### 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"
+ COMPAT_ENGINES = { 'BLENDER_LANPR' }
+
+ @classmethod
+ def poll(cls, context):
+ return (context.engine in cls.COMPAT_ENGINES)
+
+
+def lanpr_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 = "Collection Flags"
+ COMPAT_ENGINES = { 'BLENDER_LANPR', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'CYCLES' }
+
+ def draw(self, context):
+ layout=self.layout
+ collection=context.collection
+ vl = context.view_layer
+ vlc = vl.active_layer_collection
+ if vlc.name == 'Master Collection':
+ row = layout.row()
+ row.label(text="This is the master collection")
+ return
+
+ row = layout.row()
+ col = row.column(align=True)
+ col.prop(vlc,"hide_viewport")
+ col.prop(vlc,"holdout")
+ col.prop(vlc,"indirect_only")
+ row = layout.row()
+ col = row.column(align=True)
+ col.prop(collection,"hide_select")
+ col.prop(collection,"hide_viewport")
+ col.prop(collection,"hide_render")
+
+class COLLECTION_PT_lanpr_collection(CollectionButtonsPanel, Panel):
+ bl_label = "Collection LANPR"
+ COMPAT_ENGINES = { 'BLENDER_LANPR' }
+
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine == 'BLENDER_LANPR' or context.scene.lanpr.enabled
+
+ def draw_header(self, context):
+ layout = self.layout
+ collection = context.collection
+ layout.prop(collection, "configure_lanpr", text="")
+
+ def draw(self,context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+ collection = context.collection
+ if not collection.configure_lanpr:
+ return
+
+ lanpr = collection.lanpr
+ row = layout.row()
+ row.prop(lanpr,"usage")
+ if lanpr.usage!='INCLUDE':
+ layout.prop(lanpr,"force")
+ else:
+ layout.prop(lanpr,"target")
+
+ if lanpr.target:
+
+ layout.prop(lanpr,'use_multiple_levels', text="Multiple Levels")
+
+ if lanpr.use_multiple_levels:
+ col = layout.column(align=True)
+ col.prop(lanpr,'level_start',text="Level Begin")
+ col.prop(lanpr,'level_end',text="End")
+ else:
+ layout.prop(lanpr,'level_start',text="Level")
+
+ layout.prop(lanpr, "use_same_style")
+
+ if lanpr.use_same_style:
+ layout.prop_search(lanpr, 'target_layer', lanpr.target.data, "layers", icon='GREASEPENCIL')
+ layout.prop_search(lanpr, 'target_material', lanpr.target.data, "materials", icon='SHADING_TEXTURE')
+
+ expand = not lanpr.use_same_style
+ lanpr_make_line_type_entry(layout, lanpr.contour, "Contour", expand, lanpr.target.data)
+ lanpr_make_line_type_entry(layout, lanpr.crease, "Crease", expand, lanpr.target.data)
+ lanpr_make_line_type_entry(layout, lanpr.material, "Material", expand, lanpr.target.data)
+ lanpr_make_line_type_entry(layout, lanpr.edge_mark, "Edge Mark", expand, lanpr.target.data)
+ lanpr_make_line_type_entry(layout, lanpr.intersection, "Intersection", expand, lanpr.target.data)
+
+classes = (
+ COLLECTION_PT_collection_flags,
+ COLLECTION_PT_lanpr_collection,
+)
+
+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_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py
index 62dffa3b6ba..62e1bcdfce4 100644
--- a/release/scripts/startup/bl_ui/properties_data_camera.py
+++ b/release/scripts/startup/bl_ui/properties_data_camera.py
@@ -39,7 +39,7 @@ class CAMERA_PT_presets(PresetPanel, Panel):
preset_subdir = "camera"
preset_operator = "script.execute_preset"
preset_add_operator = "camera.preset_add"
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
class SAFE_AREAS_PT_presets(PresetPanel, Panel):
@@ -47,13 +47,13 @@ class SAFE_AREAS_PT_presets(PresetPanel, Panel):
preset_subdir = "safe_areas"
preset_operator = "script.execute_preset"
preset_add_operator = "safe_areas.preset_add"
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
class DATA_PT_context_camera(CameraButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
@@ -70,7 +70,7 @@ class DATA_PT_context_camera(CameraButtonsPanel, Panel):
class DATA_PT_lens(CameraButtonsPanel, Panel):
bl_label = "Lens"
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
@@ -111,7 +111,7 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
sub = col.column(align=True)
sub.prop(ccam, "longitude_min", text="Longitude Min")
sub.prop(ccam, "longitude_max", text="Max")
- elif engine in {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}:
+ elif engine in {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}:
if cam.lens_unit == 'MILLIMETERS':
col.prop(cam, "lens")
elif cam.lens_unit == 'FOV':
@@ -133,7 +133,7 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
class DATA_PT_camera_stereoscopy(CameraButtonsPanel, Panel):
bl_label = "Stereoscopy"
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
@@ -182,7 +182,7 @@ class DATA_PT_camera_stereoscopy(CameraButtonsPanel, Panel):
class DATA_PT_camera(CameraButtonsPanel, Panel):
bl_label = "Camera"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw_header_preset(self, _context):
CAMERA_PT_presets.draw_panel_header(self.layout)
@@ -212,7 +212,7 @@ class DATA_PT_camera(CameraButtonsPanel, Panel):
class DATA_PT_camera_dof(CameraButtonsPanel, Panel):
bl_label = "Depth of Field"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw_header(self, context):
cam = context.camera
@@ -237,7 +237,7 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel):
class DATA_PT_camera_dof_aperture(CameraButtonsPanel, Panel):
bl_label = "Aperture"
bl_parent_id = "DATA_PT_camera_dof"
- COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
@@ -261,7 +261,7 @@ class DATA_PT_camera_dof_aperture(CameraButtonsPanel, Panel):
class DATA_PT_camera_background_image(CameraButtonsPanel, Panel):
bl_label = "Background Images"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw_header(self, context):
cam = context.camera
@@ -368,7 +368,7 @@ class DATA_PT_camera_background_image(CameraButtonsPanel, Panel):
class DATA_PT_camera_display(CameraButtonsPanel, Panel):
bl_label = "Viewport Display"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
@@ -391,7 +391,7 @@ class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel):
bl_label = "Composition Guides"
bl_parent_id = "DATA_PT_camera_display"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
@@ -419,7 +419,7 @@ class DATA_PT_camera_display_passepartout(CameraButtonsPanel, Panel):
bl_label = "Passepartout"
bl_parent_id = "DATA_PT_camera_display"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw_header(self, context):
cam = context.camera
@@ -439,7 +439,7 @@ class DATA_PT_camera_display_passepartout(CameraButtonsPanel, Panel):
class DATA_PT_camera_safe_areas(CameraButtonsPanel, Panel):
bl_label = "Safe Areas"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw_header(self, context):
cam = context.camera
@@ -469,7 +469,7 @@ class DATA_PT_camera_safe_areas_center_cut(CameraButtonsPanel, Panel):
bl_label = "Center-Cut Safe Areas"
bl_parent_id = "DATA_PT_camera_safe_areas"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
def draw_header(self, context):
cam = context.camera
@@ -493,7 +493,7 @@ class DATA_PT_camera_safe_areas_center_cut(CameraButtonsPanel, Panel):
class DATA_PT_custom_props_camera(CameraButtonsPanel, PropertyPanel, Panel):
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
_context_path = "object.data"
_property_type = bpy.types.Camera
diff --git a/release/scripts/startup/bl_ui/properties_lanpr.py b/release/scripts/startup/bl_ui/properties_lanpr.py
new file mode 100644
index 00000000000..6bb42ec75ba
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_lanpr.py
@@ -0,0 +1,100 @@
+# ##### 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 LanprButtonsPanel:
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "lanpr"
+ COMPAT_ENGINES = { 'BLENDER_LANPR' }
+
+def lanpr_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 OBJECT_PT_lanpr_settings(LanprButtonsPanel, Panel):
+ bl_label = "LANPR settings"
+ COMPAT_ENGINES = { 'BLENDER_LANPR' }
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ obl = ob.lanpr
+ return (context.scene.render.engine == 'BLENDER_LANPR' or context.scene.lanpr.enabled) and\
+ obl.usage == 'INCLUDE' and obl.target
+
+ def draw(self,context):
+ collection = context.collection
+ lanpr = collection.lanpr
+ ob = context.object
+ obl = ob.lanpr
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ layout.prop(obl,'use_multiple_levels', text="Multiple Levels")
+ if obl.use_multiple_levels:
+ col = layout.column(align=True)
+ col.prop(obl,'level_start')
+ col.prop(obl,'level_end', text="End")
+ else:
+ layout.prop(obl,'level_start', text="Level")
+
+ layout.prop(obl,'use_same_style')
+ if obl.use_same_style:
+ layout.prop_search(obl, 'target_layer', obl.target.data, "layers", icon='GREASEPENCIL')
+ layout.prop_search(obl, 'target_material', obl.target.data, "materials", icon='SHADING_TEXTURE')
+
+ expand = not obl.use_same_style
+ lanpr_make_line_type_entry(layout, obl.contour, "Contour", expand, obl.target.data)
+ lanpr_make_line_type_entry(layout, obl.crease, "Crease", expand, obl.target.data)
+ lanpr_make_line_type_entry(layout, obl.material, "Material", expand, obl.target.data)
+ lanpr_make_line_type_entry(layout, obl.edge_mark, "Edge Mark", expand, obl.target.data)
+
+
+class OBJECT_PT_lanpr(LanprButtonsPanel, Panel):
+ bl_label = "Usage"
+ COMPAT_ENGINES = { 'BLENDER_LANPR' }
+
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine == 'BLENDER_LANPR' or context.scene.lanpr.enabled
+
+ def draw(self, context):
+ layout=self.layout
+ lanpr = context.object.lanpr
+ if context.object.type == 'MESH':
+ layout.prop(lanpr,'usage')
+ if lanpr.usage == 'INCLUDE':
+ layout.prop(lanpr, "target")
+
+
+classes = (
+ OBJECT_PT_lanpr,
+ OBJECT_PT_lanpr_settings,
+)
+
+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_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 155bbb523c7..ad7dc2a586c 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -18,12 +18,15 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
-from bpy.types import Panel
from bl_ui.space_view3d import (
VIEW3D_PT_shading_lighting,
VIEW3D_PT_shading_color,
VIEW3D_PT_shading_options,
)
+from bpy.types import (
+ Panel,
+ UIList,
+)
from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel
@@ -66,7 +69,7 @@ class RENDER_PT_color_management(RenderButtonsPanel, Panel):
bl_label = "Color Management"
bl_options = {'DEFAULT_CLOSED'}
bl_order = 100
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_LANPR'}
def draw(self, context):
layout = self.layout
@@ -99,7 +102,7 @@ class RENDER_PT_color_management_curves(RenderButtonsPanel, Panel):
bl_label = "Use Curves"
bl_parent_id = "RENDER_PT_color_management"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_LANPR'}
def draw_header(self, context):
@@ -463,7 +466,7 @@ class RENDER_PT_eevee_indirect_lighting_display(RenderButtonsPanel, Panel):
class RENDER_PT_eevee_film(RenderButtonsPanel, Panel):
bl_label = "Film"
bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_EEVEE'}
+ COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_LANPR'}
@classmethod
def poll(cls, context):
@@ -686,6 +689,308 @@ class RENDER_PT_simplify_greasepencil(RenderButtonsPanel, Panel, GreasePencilSim
bl_options = {'DEFAULT_CLOSED'}
+class LANPR_UL_linesets(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ layout.prop(item,"name", text="", emboss=False)
+
+class RENDER_PT_lanpr(RenderButtonsPanel, Panel):
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_LANPR', 'BLENDER_OPENGL', 'BLENDER_EEVEE'}
+ bl_label = "LANPR"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, context):
+ if context.scene.render.engine != 'BLENDER_LANPR':
+ self.layout.prop(context.scene.lanpr, "enabled", text="")
+
+ def draw(self, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ active_layer = lanpr.layers.active_layer
+ mode = lanpr.master_mode
+
+ layout = self.layout
+ layout.active = scene.render.engine=="BLENDER_LANPR" or lanpr.enabled
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ col = layout.column()
+
+ if scene.render.engine=="BLENDER_LANPR":
+ col.prop(lanpr, "master_mode")
+ else:
+ mode = "SOFTWARE"
+
+ if mode == "DPIX" and lanpr.shader_error:
+ layout.label(text="DPIX transform shader compile error!")
+ return
+
+ layout.prop(lanpr, "crease_threshold", slider=True)
+
+
+ col.prop(lanpr,'auto_update', text='Auto Update')
+
+ if not scene.camera:
+ has_camera=False
+ col.label(text="No active camera.")
+ else:
+ has_camera=True
+
+ c=col.column()
+ c.enabled = has_camera
+
+ if scene.render.engine=="BLENDER_LANPR":
+ txt = "Update" if mode == "SOFTWARE" else "Intersection Cache"
+ if not lanpr.auto_update:
+ c.operator("scene.lanpr_calculate", icon='FILE_REFRESH', text=txt)
+
+ if mode == "DPIX" and len(lanpr.layers)==0:
+ layout.label(text="You don't have a layer to display.")
+ layout.operator("scene.lanpr_add_line_layer");
+
+ if scene.render.engine=="BLENDER_LANPR" and mode == "SOFTWARE":
+ layout.operator("scene.lanpr_auto_create_line_layer", text = "Default", icon = "ADD")
+ row=layout.row()
+ row.template_list("LANPR_UL_linesets", "", lanpr, "layers", lanpr.layers, "active_layer_index", rows=4)
+ col=row.column(align=True)
+ if active_layer:
+ col.operator("scene.lanpr_add_line_layer", icon="ADD", text='')
+ col.operator("scene.lanpr_delete_line_layer", icon="REMOVE", text='')
+ col.separator()
+ col.operator("scene.lanpr_move_line_layer",icon='TRIA_UP', text='').direction = "UP"
+ col.operator("scene.lanpr_move_line_layer",icon='TRIA_DOWN', text='').direction = "DOWN"
+ col.separator()
+ col.operator("scene.lanpr_rebuild_all_commands",icon="FILE_REFRESH", text='')
+ else:
+ col.operator("scene.lanpr_add_line_layer", icon="ADD", text='')
+
+def lanpr_make_line_type(expand,layout,line_type,label):
+ layout.prop(line_type, "use", text=label)
+ if expand and line_type.use:
+ c = layout.column(align=True)
+ c.prop(line_type, "color", text="Color")
+ c.prop(line_type, "thickness", slider=True)
+
+class RENDER_PT_lanpr_layer_settings(RenderButtonsPanel, Panel):
+ bl_label = "Layer Settings"
+ bl_parent_id = "RENDER_PT_lanpr"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_LANPR', 'BLENDER_OPENGL', 'BLENDER_EEVEE'}
+
+ @classmethod
+ def poll(cls, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ active_layer = lanpr.layers.active_layer
+ return scene.render.engine=="BLENDER_LANPR" and active_layer
+
+ def draw(self, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ active_layer = lanpr.layers.active_layer
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ mode = lanpr.master_mode
+ if scene.render.engine!="BLENDER_LANPR" and mode != "SOFTWARE":
+ mode = "SOFTWARE"
+
+ if active_layer and mode == "DPIX":
+ active_layer = lanpr.layers[0]
+
+ if mode == "SOFTWARE":
+ layout.prop(active_layer, "use_multiple_levels", text="Multiple Levels")
+ col = layout.column(align=True)
+ col.prop(active_layer, "level_start", text='Level Start')
+ if active_layer.use_multiple_levels:
+ col.prop(active_layer, "level_end", text='End')
+
+ layout.prop(active_layer,"use_same_style")
+
+ expand = not active_layer.use_same_style
+
+ col = layout.column(align=True)
+ if not expand:
+ col.prop(active_layer, "color")
+ col.prop(active_layer, "thickness", text="Main Thickness")
+
+ lanpr_make_line_type(expand,layout,active_layer.contour,"Contour")
+ lanpr_make_line_type(expand,layout,active_layer.crease,"Crease")
+ lanpr_make_line_type(expand,layout,active_layer.edge_mark,"EdgeMark")
+ lanpr_make_line_type(expand,layout,active_layer.material_separate,"Material")
+
+ if lanpr.use_intersections:
+ lanpr_make_line_type(expand,layout,active_layer.intersection,"Intersection")
+ else:
+ layout.label(text= "Intersection calculation disabled.")
+
+class RENDER_PT_lanpr_line_normal_effects(RenderButtonsPanel, Panel):
+ bl_label = "Normal Based Line Weight"
+ bl_parent_id = "RENDER_PT_lanpr"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_LANPR', 'BLENDER_OPENGL', 'BLENDER_EEVEE'}
+
+ @classmethod
+ def poll(cls, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ active_layer = lanpr.layers.active_layer
+ return scene.render.engine=="BLENDER_LANPR" and active_layer
+
+ def draw_header(self, context):
+ active_layer = context.scene.lanpr.layers.active_layer
+ self.layout.prop(active_layer, "normal_enabled", text="")
+
+ def draw(self, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ active_layer = lanpr.layers.active_layer
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ layout.prop(active_layer,"normal_mode", text="Mode")
+ if active_layer.normal_mode != "DISABLED":
+ layout.prop(active_layer,"normal_control_object")
+ layout.prop(active_layer,"normal_effect_inverse")
+ col = layout.column(align=True)
+ col.prop(active_layer,"normal_ramp_begin")
+ col.prop(active_layer,"normal_ramp_end", text="End")
+ col = layout.column(align=True)
+ col.prop(active_layer,"normal_thickness_start", slider=True)
+ col.prop(active_layer,"normal_thickness_end", slider=True, text="End")
+
+class RENDER_PT_lanpr_line_gpu_effects(RenderButtonsPanel, Panel):
+ bl_label = "Effects"
+ bl_parent_id = "RENDER_PT_lanpr"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_LANPR', 'BLENDER_OPENGL', 'BLENDER_EEVEE'}
+
+ @classmethod
+ def poll(cls, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ active_layer = lanpr.layers.active_layer
+ return scene.render.engine=="BLENDER_LANPR" and active_layer and lanpr.master_mode == "DPIX"
+
+ def draw(self, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ active_layer = lanpr.layers.active_layer
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ col = layout.column(align = True)
+ col.prop(lanpr, "crease_threshold")
+ col.prop(lanpr, "crease_fade_threshold", text="Fade")
+ col = layout.column(align = True)
+ col.prop(lanpr, "depth_width_influence")
+ col.prop(lanpr, "depth_width_curve", text="Curve")
+ col = layout.column(align = True)
+ col.prop(lanpr, "depth_alpha_influence")
+ col.prop(lanpr, "depth_alpha_curve", text="Curve")
+
+class RENDER_PT_lanpr_gpencil(RenderButtonsPanel, Panel):
+ bl_label = "Grease Pencil"
+ bl_parent_id = "RENDER_PT_lanpr"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_LANPR', 'BLENDER_OPENGL', 'BLENDER_EEVEE'}
+
+ @classmethod
+ def poll(cls, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ return scene.render.engine!='BLENDER_LANPR'
+
+ def draw(self, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ if not scene.camera:
+ has_camera=False
+ layout.label(text="No active camera.")
+ else:
+ has_camera=True
+
+ layout.enabled=has_camera
+ layout.prop(lanpr,"auto_update", text='Auto Update')
+ layout.prop(lanpr,"gpencil_overwrite", text='Overwrite')
+ if not lanpr.auto_update:
+ layout.operator("scene.lanpr_update_gp_strokes", icon='FILE_REFRESH', text='Update Grease Pencil Targets')
+ layout.operator("scene.lanpr_bake_gp_strokes", icon='RENDER_ANIMATION', text='Bake All Frames')
+
+class RENDER_PT_lanpr_software_chain_styles(RenderButtonsPanel, Panel):
+ bl_label = "Chaining"
+ bl_parent_id = "RENDER_PT_lanpr"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_LANPR', 'BLENDER_OPENGL', 'BLENDER_EEVEE'}
+
+ @classmethod
+ def poll(cls, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+ return scene.render.engine!='BLENDER_LANPR' or lanpr.enable_chaining and (not (scene.render.engine=='BLENDER_LANPR' and lanpr.master_mode=='DPIX'))
+
+ def draw(self, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ if scene.render.engine=="BLENDER_LANPR":
+ layout.prop(lanpr, "use_same_taper", text="Taper Tips")
+ if lanpr.use_same_taper == "DISABLED":
+ col = layout.column(align = True)
+ col.prop(lanpr,"taper_left_distance")
+ col.prop(lanpr,"taper_left_strength", text="Strength")
+ col = layout.column(align = True)
+ col.prop(lanpr,"taper_right_distance")
+ col.prop(lanpr,"taper_right_strength", text="Strength")
+ else:
+ col = layout.column(align = True)
+ col.prop(lanpr,"taper_left_distance", text="Distance")
+ col.prop(lanpr,"taper_left_strength", text="Strength")
+ else:
+ layout.prop(lanpr, "chaining_geometry_threshold")
+ layout.prop(lanpr, "chaining_image_threshold")
+
+class RENDER_PT_lanpr_options(RenderButtonsPanel, Panel):
+ bl_label = "Settings"
+ bl_parent_id = "RENDER_PT_lanpr"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_LANPR', 'BLENDER_OPENGL', 'BLENDER_EEVEE'}
+
+ def draw(self, context):
+ scene = context.scene
+ lanpr = scene.lanpr
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ mode = lanpr.master_mode
+ if scene.render.engine!="BLENDER_LANPR":
+ mode = "SOFTWARE"
+
+ if mode == "DPIX":
+ layout.prop(lanpr,"gpu_cache_size")
+
+ layout.prop(lanpr,"use_intersections")
+
+ if scene.render.engine=='BLENDER_LANPR' and lanpr.master_mode=='SOFTWARE':
+ layout.prop(lanpr,"enable_chaining", text = "Chained Lines")
+
+
classes = (
RENDER_PT_context,
RENDER_PT_eevee_sampling,
@@ -717,6 +1022,14 @@ classes = (
RENDER_PT_simplify_viewport,
RENDER_PT_simplify_render,
RENDER_PT_simplify_greasepencil,
+ RENDER_PT_lanpr,
+ RENDER_PT_lanpr_layer_settings,
+ RENDER_PT_lanpr_gpencil,
+ RENDER_PT_lanpr_line_normal_effects,
+ RENDER_PT_lanpr_line_gpu_effects,
+ RENDER_PT_lanpr_software_chain_styles,
+ RENDER_PT_lanpr_options,
+ LANPR_UL_linesets,
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/properties_world.py b/release/scripts/startup/bl_ui/properties_world.py
index 6f00e521e58..4a9f35ee59a 100644
--- a/release/scripts/startup/bl_ui/properties_world.py
+++ b/release/scripts/startup/bl_ui/properties_world.py
@@ -37,7 +37,7 @@ class WorldButtonsPanel:
class WORLD_PT_context_world(WorldButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
@@ -83,7 +83,7 @@ class EEVEE_WORLD_PT_mist(WorldButtonsPanel, Panel):
class WORLD_PT_custom_props(WorldButtonsPanel, PropertyPanel, Panel):
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_LANPR', 'BLENDER_WORKBENCH'}
_context_path = "world"
_property_type = bpy.types.World
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index ed9a27aeae6..6dc21669f32 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -3774,9 +3774,9 @@ 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()
-
+ scene = context.scene
+ if render.use_freestyle or scene.lanpr.enabled or scene.render.engine=="BLENDER_LANPR":
+ layout.separator()
col.operator("mesh.mark_freestyle_edge").clear = False
col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
@@ -3971,9 +3971,8 @@ class VIEW3D_MT_edit_mesh_edges_data(Menu):
props.use_verts = True
props.clear = True
- if render.use_freestyle:
+ if render.use_freestyle or context.scene.lanpr.enabled or render.engine=="BLENDER_LANPR":
layout.separator()
-
layout.operator("mesh.mark_freestyle_edge").clear = False
layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
@@ -3981,7 +3980,7 @@ class VIEW3D_MT_edit_mesh_edges_data(Menu):
class VIEW3D_MT_edit_mesh_edges(Menu):
bl_label = "Edge"
- def draw(self, _context):
+ def draw(self, context):
layout = self.layout
with_freestyle = bpy.app.build_options.freestyle
@@ -4029,9 +4028,9 @@ class VIEW3D_MT_edit_mesh_edges(Menu):
props.use_verts = True
props.clear = True
- if with_freestyle:
+ scene = context.scene
+ if with_freestyle or scene.lanpr.enabled or scene.render.engine=="BLENDER_LANPR":
layout.separator()
-
layout.operator("mesh.mark_freestyle_edge").clear = False
layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
@@ -4039,7 +4038,7 @@ class VIEW3D_MT_edit_mesh_edges(Menu):
class VIEW3D_MT_edit_mesh_faces_data(Menu):
bl_label = "Face Data"
- def draw(self, _context):
+ def draw(self, context):
layout = self.layout
with_freestyle = bpy.app.build_options.freestyle
@@ -4054,9 +4053,9 @@ class VIEW3D_MT_edit_mesh_faces_data(Menu):
layout.operator("mesh.uvs_rotate")
layout.operator("mesh.uvs_reverse")
- layout.separator()
-
- if with_freestyle:
+ scene = context.scene
+ if with_freestyle or scene.lanpr.enabled or scene.render.engine=="BLENDER_LANPR":
+ layout.separator()
layout.operator("mesh.mark_freestyle_face").clear = False
layout.operator("mesh.mark_freestyle_face", text="Clear Freestyle Face").clear = True
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index faa331aa02d..2084122929e 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -194,6 +194,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);
@@ -213,6 +217,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_scene.h b/source/blender/blenkernel/BKE_scene.h
index dca6f569e25..6ca533db144 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -257,6 +257,11 @@ void BKE_scene_cursor_from_mat4(struct View3DCursor *cursor,
*/
void BKE_scene_eval_sequencer_sequences(struct Depsgraph *depsgraph, struct Scene *scene);
+/* LANPR */
+
+void BKE_lanpr_copy_data(const struct Scene *from, struct Scene *to);
+void BKE_lanpr_free_everything(struct Scene *s);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 817fe849eab..f6f18f4ab2a 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -632,6 +632,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_ALEMBIC)
list(APPEND INC
../io/alembic
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index c1d77fd5f9e..a28cc73cb94 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -216,6 +216,9 @@ Collection *BKE_collection_add(Main *bmain, Collection *collection_parent, const
void BKE_collection_free(Collection *collection)
{
collection_free_data(&collection->id);
+
+ /* Remove LANPR configurations */
+ MEM_SAFE_FREE(collection->lanpr);
}
/**
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 4311e425abf..7f65dfc2b51 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -1176,6 +1176,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 the active gp-layer */
void BKE_gpencil_layer_active_set(bGPdata *gpd, bGPDlayer *active)
{
@@ -1739,6 +1764,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 */
void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
{
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 8e65864e460..7d60d21a496 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -101,6 +101,9 @@
#include "DEG_depsgraph_debug.h"
#include "DEG_depsgraph_query.h"
+/* lanpr scene cache free function needs this. */
+#include "DRW_engine.h"
+
#include "RE_engine.h"
#include "engines/eevee/eevee_lightcache.h"
@@ -591,6 +594,7 @@ IDTypeInfo IDType_ID_SCE = {
const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE";
const char *RE_engine_id_BLENDER_WORKBENCH = "BLENDER_WORKBENCH";
+const char *RE_engine_id_BLENDER_LANPR = "BLENDER_LANPR";
const char *RE_engine_id_CYCLES = "CYCLES";
void free_avicodecdata(AviCodecData *acd)
@@ -741,6 +745,144 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
MEM_freeN(toolsettings);
}
+void BKE_lanpr_copy_data(const Scene *from, Scene *to)
+{
+ const SceneLANPR *lanpr = &from->lanpr;
+ LANPR_LineLayer *ll, *new_ll;
+
+ to->lanpr.line_layers.first = to->lanpr.line_layers.last = NULL;
+ memset(&to->lanpr.line_layers, 0, sizeof(ListBase));
+
+ for (ll = lanpr->line_layers.first; ll; ll = ll->next) {
+ new_ll = MEM_callocN(sizeof(LANPR_LineLayer), "Copied Line Layer");
+ memcpy(new_ll, ll, sizeof(LANPR_LineLayer));
+ new_ll->next = new_ll->prev = NULL;
+ new_ll->batch = NULL;
+ BLI_addtail(&to->lanpr.line_layers, new_ll);
+ }
+
+ /* render_buffer now only accessible from lanpr_share */
+}
+
+/**
+ * Only copy internal data of Scene ID from source
+ * to already allocated/initialized destination.
+ * You probably never want to use that directly,
+ * use #BKE_id_copy or #BKE_id_copy_ex for typical needs.
+ *
+ * WARNING! This function will not handle ID user count!
+ *
+ * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
+ */
+void BKE_scene_copy_data(Main *bmain, Scene *sce_dst, const Scene *sce_src, const int flag)
+{
+ /* We never handle usercount here for own data. */
+ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
+ /* We always need allocation of our private ID data. */
+ const int flag_private_id_data = flag & ~LIB_ID_CREATE_NO_ALLOCATE;
+
+ sce_dst->ed = NULL;
+ sce_dst->depsgraph_hash = NULL;
+ sce_dst->fps_info = NULL;
+
+ /* Master Collection */
+ if (sce_src->master_collection) {
+ BKE_id_copy_ex(bmain,
+ (ID *)sce_src->master_collection,
+ (ID **)&sce_dst->master_collection,
+ flag_private_id_data);
+ }
+
+ /* View Layers */
+ BLI_duplicatelist(&sce_dst->view_layers, &sce_src->view_layers);
+ for (ViewLayer *view_layer_src = sce_src->view_layers.first,
+ *view_layer_dst = sce_dst->view_layers.first;
+ view_layer_src;
+ view_layer_src = view_layer_src->next, view_layer_dst = view_layer_dst->next) {
+ BKE_view_layer_copy_data(sce_dst, sce_src, view_layer_dst, view_layer_src, flag_subdata);
+ }
+
+ BLI_duplicatelist(&(sce_dst->markers), &(sce_src->markers));
+ BLI_duplicatelist(&(sce_dst->transform_spaces), &(sce_src->transform_spaces));
+ BLI_duplicatelist(&(sce_dst->r.views), &(sce_src->r.views));
+ BKE_keyingsets_copy(&(sce_dst->keyingsets), &(sce_src->keyingsets));
+
+ if (sce_src->nodetree) {
+ BKE_id_copy_ex(
+ bmain, (ID *)sce_src->nodetree, (ID **)&sce_dst->nodetree, flag_private_id_data);
+ BKE_libblock_relink_ex(bmain,
+ sce_dst->nodetree,
+ (void *)(&sce_src->id),
+ &sce_dst->id,
+ ID_REMAP_SKIP_NEVER_NULL_USAGE);
+ }
+
+ if (sce_src->rigidbody_world) {
+ sce_dst->rigidbody_world = BKE_rigidbody_world_copy(sce_src->rigidbody_world, flag_subdata);
+ }
+
+ /* copy color management settings */
+ BKE_color_managed_display_settings_copy(&sce_dst->display_settings, &sce_src->display_settings);
+ BKE_color_managed_view_settings_copy(&sce_dst->view_settings, &sce_src->view_settings);
+ BKE_color_managed_colorspace_settings_copy(&sce_dst->sequencer_colorspace_settings,
+ &sce_src->sequencer_colorspace_settings);
+
+ BKE_color_managed_display_settings_copy(&sce_dst->r.im_format.display_settings,
+ &sce_src->r.im_format.display_settings);
+ BKE_color_managed_view_settings_copy(&sce_dst->r.im_format.view_settings,
+ &sce_src->r.im_format.view_settings);
+
+ BKE_color_managed_display_settings_copy(&sce_dst->r.bake.im_format.display_settings,
+ &sce_src->r.bake.im_format.display_settings);
+ BKE_color_managed_view_settings_copy(&sce_dst->r.bake.im_format.view_settings,
+ &sce_src->r.bake.im_format.view_settings);
+
+ BKE_curvemapping_copy_data(&sce_dst->r.mblur_shutter_curve, &sce_src->r.mblur_shutter_curve);
+
+ /* tool settings */
+ sce_dst->toolsettings = BKE_toolsettings_copy(sce_dst->toolsettings, flag_subdata);
+
+ /* make a private copy of the avicodecdata */
+ if (sce_src->r.avicodecdata) {
+ sce_dst->r.avicodecdata = MEM_dupallocN(sce_src->r.avicodecdata);
+ sce_dst->r.avicodecdata->lpFormat = MEM_dupallocN(sce_dst->r.avicodecdata->lpFormat);
+ sce_dst->r.avicodecdata->lpParms = MEM_dupallocN(sce_dst->r.avicodecdata->lpParms);
+ }
+
+ if (sce_src->r.ffcodecdata.properties) {
+ /* intentionally check sce_dst not sce_src. */ /* XXX ??? comment outdated... */
+ sce_dst->r.ffcodecdata.properties = IDP_CopyProperty_ex(sce_src->r.ffcodecdata.properties,
+ flag_subdata);
+ }
+
+ if (sce_src->display.shading.prop) {
+ sce_dst->display.shading.prop = IDP_CopyProperty(sce_src->display.shading.prop);
+ }
+
+ BKE_sound_reset_scene_runtime(sce_dst);
+
+ /* Copy sequencer, this is local data! */
+ if (sce_src->ed) {
+ sce_dst->ed = MEM_callocN(sizeof(*sce_dst->ed), __func__);
+ sce_dst->ed->seqbasep = &sce_dst->ed->seqbase;
+ BKE_sequence_base_dupli_recursive(sce_src,
+ sce_dst,
+ &sce_dst->ed->seqbase,
+ &sce_src->ed->seqbase,
+ SEQ_DUPE_ALL,
+ flag_subdata);
+ }
+
+ if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
+ BKE_previewimg_id_copy(&sce_dst->id, &sce_src->id);
+ }
+ else {
+ sce_dst->preview = NULL;
+ }
+
+ BKE_scene_copy_data_eevee(sce_dst, sce_src);
+}
+
void BKE_scene_copy_data_eevee(Scene *sce_dst, const Scene *sce_src)
{
/* Copy eevee data between scenes. */
@@ -748,6 +890,10 @@ void BKE_scene_copy_data_eevee(Scene *sce_dst, const Scene *sce_src)
sce_dst->eevee.light_cache_data = NULL;
sce_dst->eevee.light_cache_info[0] = '\0';
/* TODO Copy the cache. */
+
+ /* lanpr data */
+
+ BKE_lanpr_copy_data(sce_src, sce_dst);
}
Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type)
@@ -874,6 +1020,303 @@ void BKE_scene_groups_relink(Scene *sce)
}
}
+void BKE_scene_make_local(Main *bmain, Scene *sce, const bool lib_local)
+{
+ /* For now should work, may need more work though to support all possible corner cases
+ * (also scene_copy probably needs some love). */
+ BKE_lib_id_make_local(bmain, &sce->id, true, lib_local);
+}
+
+void BKE_lanpr_free_everything(Scene *s)
+{
+ SceneLANPR *lanpr = &s->lanpr;
+ LANPR_LineLayer *ll;
+
+#ifdef WITH_LANPR
+ DRW_scene_lanpr_freecache(s);
+#endif
+
+ while ((ll = BLI_pophead(&lanpr->line_layers)) != NULL) {
+ MEM_freeN(ll);
+ }
+}
+
+/** Free (or release) any data used by this scene (does not free the scene itself). */
+void BKE_scene_free_ex(Scene *sce, const bool do_id_user)
+{
+ BKE_animdata_free((ID *)sce, false);
+
+ BKE_sequencer_editing_free(sce, do_id_user);
+
+ BKE_keyingsets_free(&sce->keyingsets);
+
+ /* is no lib link block, but scene extension */
+ if (sce->nodetree) {
+ ntreeFreeEmbeddedTree(sce->nodetree);
+ MEM_freeN(sce->nodetree);
+ sce->nodetree = NULL;
+ }
+
+ if (sce->rigidbody_world) {
+ BKE_rigidbody_free_world(sce);
+ }
+
+ if (sce->r.avicodecdata) {
+ free_avicodecdata(sce->r.avicodecdata);
+ MEM_freeN(sce->r.avicodecdata);
+ sce->r.avicodecdata = NULL;
+ }
+ if (sce->r.ffcodecdata.properties) {
+ IDP_FreeProperty(sce->r.ffcodecdata.properties);
+ sce->r.ffcodecdata.properties = NULL;
+ }
+
+ BLI_freelistN(&sce->markers);
+ BLI_freelistN(&sce->transform_spaces);
+ BLI_freelistN(&sce->r.views);
+
+ BKE_toolsettings_free(sce->toolsettings);
+ sce->toolsettings = NULL;
+
+ BKE_scene_free_depsgraph_hash(sce);
+
+ MEM_SAFE_FREE(sce->fps_info);
+
+ BKE_sound_destroy_scene(sce);
+
+ BKE_color_managed_view_settings_free(&sce->view_settings);
+
+ BKE_previewimg_free(&sce->preview);
+ BKE_curvemapping_free_data(&sce->r.mblur_shutter_curve);
+
+ for (ViewLayer *view_layer = sce->view_layers.first, *view_layer_next; view_layer;
+ view_layer = view_layer_next) {
+ view_layer_next = view_layer->next;
+
+ BLI_remlink(&sce->view_layers, view_layer);
+ BKE_view_layer_free_ex(view_layer, do_id_user);
+ }
+
+ /* Master Collection */
+ // TODO: what to do with do_id_user? it's also true when just
+ // closing the file which seems wrong? should decrement users
+ // for objects directly in the master collection? then other
+ // collections in the scene need to do it too?
+ if (sce->master_collection) {
+ BKE_collection_free(sce->master_collection);
+ MEM_freeN(sce->master_collection);
+ sce->master_collection = NULL;
+ }
+
+ if (sce->eevee.light_cache) {
+ EEVEE_lightcache_free(sce->eevee.light_cache);
+ sce->eevee.light_cache = NULL;
+ }
+
+ if (sce->display.shading.prop) {
+ IDP_FreeProperty(sce->display.shading.prop);
+ sce->display.shading.prop = NULL;
+ }
+
+ /* Copied and generated LANPR data. */
+ BKE_lanpr_free_everything(sce);
+
+ /* These are freed on doversion. */
+ BLI_assert(sce->layer_properties == NULL);
+}
+
+void BKE_scene_free(Scene *sce)
+{
+ BKE_scene_free_ex(sce, true);
+}
+
+/**
+ * \note Use DNA_scene_defaults.h where possible.
+ */
+void BKE_scene_init(Scene *sce)
+{
+ const char *colorspace_name;
+ SceneRenderView *srv;
+ CurveMapping *mblur_shutter_curve;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(sce, id));
+
+ MEMCPY_STRUCT_AFTER(sce, DNA_struct_default_get(Scene), id);
+
+ BLI_strncpy(sce->r.bake.filepath, U.renderdir, sizeof(sce->r.bake.filepath));
+
+ mblur_shutter_curve = &sce->r.mblur_shutter_curve;
+ BKE_curvemapping_set_defaults(mblur_shutter_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f);
+ BKE_curvemapping_initialize(mblur_shutter_curve);
+ BKE_curvemap_reset(mblur_shutter_curve->cm,
+ &mblur_shutter_curve->clipr,
+ CURVE_PRESET_MAX,
+ CURVEMAP_SLOPE_POS_NEG);
+
+ sce->toolsettings = DNA_struct_default_alloc(ToolSettings);
+
+ sce->toolsettings->autokey_mode = U.autokey_mode;
+
+ /* grease pencil multiframe falloff curve */
+ sce->toolsettings->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ CurveMapping *gp_falloff_curve = sce->toolsettings->gp_sculpt.cur_falloff;
+ BKE_curvemapping_initialize(gp_falloff_curve);
+ BKE_curvemap_reset(
+ gp_falloff_curve->cm, &gp_falloff_curve->clipr, CURVE_PRESET_GAUSS, CURVEMAP_SLOPE_POSITIVE);
+
+ sce->toolsettings->gp_sculpt.cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ CurveMapping *gp_primitive_curve = sce->toolsettings->gp_sculpt.cur_primitive;
+ BKE_curvemapping_initialize(gp_primitive_curve);
+ BKE_curvemap_reset(gp_primitive_curve->cm,
+ &gp_primitive_curve->clipr,
+ CURVE_PRESET_BELL,
+ CURVEMAP_SLOPE_POSITIVE);
+
+ sce->unit.system = USER_UNIT_METRIC;
+ sce->unit.scale_length = 1.0f;
+ sce->unit.length_unit = bUnit_GetBaseUnitOfType(USER_UNIT_METRIC, B_UNIT_LENGTH);
+ sce->unit.mass_unit = bUnit_GetBaseUnitOfType(USER_UNIT_METRIC, B_UNIT_MASS);
+ sce->unit.time_unit = bUnit_GetBaseUnitOfType(USER_UNIT_METRIC, B_UNIT_TIME);
+
+ {
+ ParticleEditSettings *pset;
+ pset = &sce->toolsettings->particle;
+ for (int i = 1; i < ARRAY_SIZE(pset->brush); i++) {
+ pset->brush[i] = pset->brush[0];
+ }
+ pset->brush[PE_BRUSH_CUT].strength = 1.0f;
+ }
+
+ BLI_strncpy(sce->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(sce->r.engine));
+
+ BLI_strncpy(sce->r.pic, U.renderdir, sizeof(sce->r.pic));
+
+ /* Note; in header_info.c the scene copy happens...,
+ * if you add more to renderdata it has to be checked there. */
+
+ /* multiview - stereo */
+ BKE_scene_add_render_view(sce, STEREO_LEFT_NAME);
+ srv = sce->r.views.first;
+ BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix));
+
+ BKE_scene_add_render_view(sce, STEREO_RIGHT_NAME);
+ srv = sce->r.views.last;
+ BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix));
+
+ BKE_sound_reset_scene_runtime(sce);
+
+ /* color management */
+ colorspace_name = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_SEQUENCER);
+
+ BKE_color_managed_display_settings_init(&sce->display_settings);
+ BKE_color_managed_view_settings_init_render(
+ &sce->view_settings, &sce->display_settings, "Filmic");
+ BLI_strncpy(sce->sequencer_colorspace_settings.name,
+ colorspace_name,
+ sizeof(sce->sequencer_colorspace_settings.name));
+
+ /* Those next two sets (render and baking settings) are not currently in use,
+ * but are exposed to RNA API and hence must have valid data. */
+ BKE_color_managed_display_settings_init(&sce->r.im_format.display_settings);
+ BKE_color_managed_view_settings_init_render(
+ &sce->r.im_format.view_settings, &sce->r.im_format.display_settings, "Filmic");
+
+ BKE_color_managed_display_settings_init(&sce->r.bake.im_format.display_settings);
+ BKE_color_managed_view_settings_init_render(
+ &sce->r.bake.im_format.view_settings, &sce->r.bake.im_format.display_settings, "Filmic");
+
+ /* GP Sculpt brushes */
+ {
+ GP_Sculpt_Settings *gset = &sce->toolsettings->gp_sculpt;
+ GP_Sculpt_Data *gp_brush;
+ float curcolor_add[3], curcolor_sub[3];
+ ARRAY_SET_ITEMS(curcolor_add, 1.0f, 0.6f, 0.6f);
+ ARRAY_SET_ITEMS(curcolor_sub, 0.6f, 0.6f, 1.0f);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH];
+ gp_brush->size = 25;
+ gp_brush->strength = 0.3f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_SMOOTH_PRESSURE |
+ GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_THICKNESS];
+ gp_brush->size = 25;
+ gp_brush->strength = 0.5f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_STRENGTH];
+ gp_brush->size = 25;
+ gp_brush->strength = 0.5f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_GRAB];
+ gp_brush->size = 50;
+ gp_brush->strength = 0.3f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_PUSH];
+ gp_brush->size = 25;
+ gp_brush->strength = 0.3f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_TWIST];
+ gp_brush->size = 50;
+ gp_brush->strength = 0.3f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_PINCH];
+ gp_brush->size = 50;
+ gp_brush->strength = 0.5f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+
+ gp_brush = &gset->brush[GP_SCULPT_TYPE_RANDOMIZE];
+ gp_brush->size = 25;
+ gp_brush->strength = 0.5f;
+ gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
+ copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
+ copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
+ }
+
+ /* Curve Profile */
+ sce->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE);
+
+ for (int i = 0; i < ARRAY_SIZE(sce->orientation_slots); i++) {
+ sce->orientation_slots[i].index_custom = -1;
+ }
+
+ /* Master Collection */
+ sce->master_collection = BKE_collection_master_add();
+
+ BKE_view_layer_add(sce, "View Layer", NULL, VIEWLAYER_ADD_NEW);
+
+ /* SceneLANPR */
+
+ sce->lanpr.crease_threshold = 0.7;
+
+ sce->lanpr.line_color[0] = 1;
+ sce->lanpr.line_color[1] = 1;
+ sce->lanpr.line_color[2] = 1;
+ sce->lanpr.line_color[3] = 1;
+
+ sce->lanpr.flags |= (LANPR_USE_CHAINING | LANPR_USE_INTERSECTIONS);
+ sce->lanpr.chaining_image_threshold = 0.01;
+ sce->lanpr.chaining_geometry_threshold = 0.1;
+}
+
Scene *BKE_scene_add(Main *bmain, const char *name)
{
Scene *sce;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index d3c391a1595..db63091ce36 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -62,6 +62,7 @@
#include "DNA_hair_types.h"
#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
+#include "DNA_lanpr_types.h"
#include "DNA_lattice_types.h"
#include "DNA_layer_types.h"
#include "DNA_light_types.h"
@@ -6463,6 +6464,8 @@ static void direct_link_collection(FileData *fd, Collection *collection)
direct_link_view_layer(fd, collection->view_layer);
}
#endif
+
+ collection->lanpr = newdataadr(fd, collection->lanpr);
}
static void lib_link_collection_data(FileData *fd, Library *lib, Collection *collection)
@@ -6480,6 +6483,10 @@ static void lib_link_collection_data(FileData *fd, Library *lib, Collection *col
child->collection = newlibadr(fd, lib, child->collection);
}
+ if (collection->lanpr) {
+ collection->lanpr->target = newlibadr(fd, lib, collection->lanpr->target);
+ }
+
BKE_collection_parent_relations_rebuild(collection);
}
@@ -6761,6 +6768,42 @@ static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce)
}
}
+ if (sce->rigidbody_world) {
+ RigidBodyWorld *rbw = sce->rigidbody_world;
+ if (rbw->group) {
+ rbw->group = newlibadr(fd, sce->id.lib, rbw->group);
+ }
+ if (rbw->constraints) {
+ rbw->constraints = newlibadr(fd, sce->id.lib, rbw->constraints);
+ }
+ if (rbw->effector_weights) {
+ rbw->effector_weights->group = newlibadr(fd, sce->id.lib, rbw->effector_weights->group);
+ }
+ }
+
+ if (sce->nodetree) {
+ lib_link_ntree(fd, &sce->id, sce->nodetree);
+ sce->nodetree->id.lib = sce->id.lib;
+ composite_patch(sce->nodetree, sce);
+ }
+
+ for (SceneRenderLayer *srl = sce->r.layers.first; srl; srl = srl->next) {
+ srl->mat_override = newlibadr(fd, sce->id.lib, srl->mat_override);
+ for (FreestyleModuleConfig *fmc = srl->freestyleConfig.modules.first; fmc; fmc = fmc->next) {
+ fmc->script = newlibadr(fd, sce->id.lib, fmc->script);
+ }
+ for (FreestyleLineSet *fls = srl->freestyleConfig.linesets.first; fls; fls = fls->next) {
+ fls->linestyle = newlibadr(fd, sce->id.lib, fls->linestyle);
+ fls->group = newlibadr(fd, sce->id.lib, fls->group);
+ }
+ }
+
+ for (LANPR_LineLayer *ll = sce->lanpr.line_layers.first; ll; ll = ll->next) {
+ ll->normal_control_object = newlibadr(fd, sce->id.lib, ll->normal_control_object);
+ }
+
+ /* Motion Tracking */
+ sce->clip = newlibadr(fd, sce->id.lib, sce->clip);
if (sce->nodetree) {
composite_patch(sce->nodetree, sce);
}
@@ -7202,6 +7245,12 @@ static void direct_link_scene(FileData *fd, Scene *sce)
}
EEVEE_lightcache_info_update(&sce->eevee);
+ sce->lanpr.active_layer = newdataadr(fd, sce->lanpr.active_layer);
+ link_list(fd, &(sce->lanpr.line_layers));
+ for (LANPR_LineLayer *ll = sce->lanpr.line_layers.first; ll; ll = ll->next) {
+ ll->batch = NULL;
+ ll->shgrp = NULL;
+ }
direct_link_view3dshading(fd, &sce->display.shading);
sce->layer_properties = newdataadr(fd, sce->layer_properties);
@@ -11031,6 +11080,8 @@ static void expand_collection(FileData *fd, Main *mainvar, Collection *collectio
expand_doit(fd, mainvar, child->collection);
}
+ expand_doit(fd, mainvar, collection->lanpr->target);
+
#ifdef USE_COLLECTION_COMPAT_28
if (collection->collection != NULL) {
expand_scene_collection(fd, mainvar, collection->collection);
@@ -11450,6 +11501,12 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
}
}
+ for (LANPR_LineLayer *ll = sce->lanpr.line_layers.first; ll; ll = ll->next) {
+ if (ll->normal_control_object) {
+ expand_doit(fd, mainvar, ll->normal_control_object);
+ }
+ }
+
if (sce->gpd) {
expand_doit(fd, mainvar, sce->gpd);
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 96e8981a944..de8391de6c8 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -4421,6 +4421,19 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
ToolSettings *ts = scene->toolsettings;
UnifiedPaintSettings *ups = &ts->unified_paint_settings;
ups->flag &= ~(UNIFIED_PAINT_FLAG_UNUSED_0 | UNIFIED_PAINT_FLAG_UNUSED_1);
+
+ if (!DNA_struct_find(fd->filesdna, "SceneLANPR")) {
+ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
+
+ scene->lanpr.crease_threshold = 0.7;
+
+ scene->lanpr.flags |= (LANPR_USE_CHAINING | LANPR_USE_INTERSECTIONS);
+
+ zero_v4(scene->lanpr.line_color);
+
+ scene->lanpr.line_color[3] = 1;
+ }
+ }
}
/* Set the default render pass in the viewport to Combined. */
@@ -5077,4 +5090,4 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* Keep this block, even when empty. */
}
-}
+} \ No newline at end of file
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index e3b4166a4bf..c7d9fbf7268 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -126,6 +126,8 @@
#include "DNA_object_types.h"
#include "DNA_packedFile_types.h"
#include "DNA_particle_types.h"
+#include "DNA_lightprobe_types.h"
+#include "DNA_lanpr_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
@@ -2461,6 +2463,10 @@ static void write_collection_nolib(WriteData *wd, Collection *collection)
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
writestruct(wd, DATA, CollectionChild, 1, child);
}
+
+ if (collection->lanpr != NULL) {
+ writestruct(wd, DATA, CollectionLANPR, 1, collection->lanpr);
+ }
}
static void write_collection(WriteData *wd, Collection *collection, const void *id_address)
@@ -2813,6 +2819,11 @@ static void write_scene(WriteData *wd, Scene *sce, const void *id_address)
write_lightcache(wd, sce->eevee.light_cache_data);
}
+ /* LANPR Line Layers */
+ for (LANPR_LineLayer *ll = sce->lanpr.line_layers.first; ll; ll = ll->next) {
+ writestruct(wd, DATA, LANPR_LineLayer, 1, ll);
+ }
+
write_view3dshading(wd, &sce->display.shading);
/* Freed on doversion. */
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 0214a8e1887..d9f7a3acae0 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -184,6 +184,20 @@ set(LIB
bf_windowmanager
)
+if(WITH_LANPR)
+ list(APPEND INC
+ engines/lanpr
+ )
+ list(APPEND SRC
+ engines/lanpr/lanpr_all.h
+ engines/lanpr/lanpr_dpix.c
+ engines/lanpr/lanpr_engine.c
+ engines/lanpr/lanpr_cpu.c
+ engines/lanpr/lanpr_chain_draw.c
+ )
+ add_definitions(-DWITH_LANPR)
+endif()
+
data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC)
@@ -306,6 +320,16 @@ data_to_c_simple(engines/basic/shaders/conservative_depth_geom.glsl SRC)
data_to_c_simple(engines/basic/shaders/conservative_depth_vert.glsl SRC)
data_to_c_simple(engines/basic/shaders/conservative_depth_frag.glsl SRC)
+if(WITH_LANPR)
+ data_to_c_simple(engines/lanpr/shaders/lanpr_dpix_preview_geom.glsl SRC)
+ data_to_c_simple(engines/lanpr/shaders/lanpr_dpix_preview_frag.glsl SRC)
+ data_to_c_simple(engines/lanpr/shaders/lanpr_dpix_project_clip_frag.glsl SRC)
+ data_to_c_simple(engines/lanpr/shaders/lanpr_dpix_project_passthrough_vert.glsl SRC)
+ data_to_c_simple(engines/lanpr/shaders/lanpr_software_line_chain_geom.glsl SRC)
+ data_to_c_simple(engines/lanpr/shaders/lanpr_software_passthrough_vert.glsl SRC)
+ data_to_c_simple(engines/lanpr/shaders/lanpr_software_chain_geom.glsl SRC)
+endif()
+
data_to_c_simple(engines/overlay/shaders/antialiasing_frag.glsl SRC)
data_to_c_simple(engines/overlay/shaders/antialiasing_vert.glsl SRC)
data_to_c_simple(engines/overlay/shaders/armature_dof_vert.glsl SRC)
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index 6c835c6d7ae..332db784ee7 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -133,6 +133,11 @@ void DRW_draw_select_id(struct Depsgraph *depsgraph,
bool DRW_render_check_grease_pencil(struct Depsgraph *depsgraph);
void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph);
+/* LANPR calls */
+#ifdef WITH_LANPR
+void DRW_scene_lanpr_freecache(struct Scene *sce);
+#endif
+
/* This is here because GPUViewport needs it */
struct DRWInstanceDataList *DRW_instance_data_list_create(void);
void DRW_instance_data_list_free(struct DRWInstanceDataList *idatalist);
diff --git a/source/blender/draw/engines/lanpr/lanpr_all.h b/source/blender/draw/engines/lanpr/lanpr_all.h
new file mode 100644
index 00000000000..dee6b1853dc
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/lanpr_all.h
@@ -0,0 +1,247 @@
+/*
+ * 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.
+ *
+ * Copyright 2019, Blender Foundation.
+ *
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#ifndef __LANPR_ALL_H__
+#define __LANPR_ALL_H__
+
+#include "BLI_mempool.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_customdata.h"
+#include "BKE_object.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_listBase.h"
+#include "DNA_lanpr_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "DRW_render.h"
+
+#include "ED_lanpr.h"
+
+#include "GPU_batch.h"
+#include "GPU_draw.h"
+#include "GPU_framebuffer.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+#include "GPU_uniformbuffer.h"
+#include "GPU_viewport.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "bmesh.h"
+
+#define LANPR_ENGINE "BLENDER_LANPR"
+
+extern struct RenderEngineType DRW_engine_viewport_lanpr_type;
+extern struct DrawEngineType draw_engine_lanpr_type;
+
+typedef struct LANPR_RenderBuffer LANPR_RenderBuffer;
+
+typedef struct LANPR_PassList {
+ /* Image filtering */
+ struct DRWPass *depth_pass;
+ struct DRWPass *color_pass;
+ struct DRWPass *normal_pass;
+ struct DRWPass *edge_intermediate;
+ struct DRWPass *edge_thinning;
+
+ /* GPU */
+ struct DRWPass *dpix_transform_pass;
+ struct DRWPass *dpix_preview_pass;
+
+ /* SOFTWARE */
+ struct DRWPass *software_pass;
+
+} LANPR_PassList;
+
+typedef struct LANPR_FramebufferList {
+
+ /* CPU */
+ struct GPUFrameBuffer *passes;
+ struct GPUFrameBuffer *edge_intermediate;
+ struct GPUFrameBuffer *edge_thinning;
+
+ /* GPU */
+ struct GPUFrameBuffer *dpix_transform;
+ struct GPUFrameBuffer *dpix_preview;
+
+ /* Image filtering */
+ struct GPUFrameBuffer *software_ms;
+
+} LANPR_FramebufferList;
+
+typedef struct LANPR_TextureList {
+
+ struct GPUTexture *color;
+ struct GPUTexture *normal;
+ struct GPUTexture *depth;
+ struct GPUTexture *edge_intermediate;
+
+ struct GPUTexture *dpix_in_pl;
+ struct GPUTexture *dpix_in_pr;
+ struct GPUTexture *dpix_in_nl;
+ struct GPUTexture *dpix_in_nr;
+
+ /** RGBA texture format,
+ * R:Material, G: Freestyle Edge Mark,
+ * BA:Reserved for future usages */
+ struct GPUTexture *dpix_in_edge_mask;
+
+ struct GPUTexture *dpix_out_pl;
+ struct GPUTexture *dpix_out_pr;
+ struct GPUTexture *dpix_out_length;
+
+ /** Multisample resolve */
+ struct GPUTexture *ms_resolve_depth;
+ struct GPUTexture *ms_resolve_color;
+
+} LANPR_TextureList;
+
+typedef struct LANPR_PrivateData {
+ DRWShadingGroup *multipass_shgrp;
+ DRWShadingGroup *edge_detect_shgrp;
+ DRWShadingGroup *edge_thinning_shgrp;
+
+ DRWShadingGroup *dpix_transform_shgrp;
+ DRWShadingGroup *dpix_preview_shgrp;
+
+ DRWShadingGroup *debug_shgrp;
+
+ /* Image filtering */
+
+ float normal_clamp;
+ float normal_strength;
+ float depth_clamp;
+ float depth_strength;
+
+ float zfar;
+ float znear;
+
+ /** Thinning stage */
+ int stage;
+
+ float *line_result;
+ unsigned char *line_result_8bit;
+
+ /** If not match then recreate buffer. */
+ int width, height;
+ void **sample_table;
+
+ ListBase pending_samples;
+ ListBase erased_samples;
+ ListBase line_strips;
+
+ /* dpix data */
+
+ void *atlas_pl;
+ void *atlas_pr;
+ void *atlas_nl;
+ void *atlas_nr;
+ void *atlas_edge_mask;
+
+ int begin_index;
+
+ int dpix_sample_step;
+ int dpix_is_perspective;
+ float dpix_viewport[4];
+ float output_viewport[4];
+ int dpix_buffer_width;
+ float dpix_depth_offset;
+
+ float dpix_znear;
+ float dpix_zfar;
+
+ /* drawing */
+
+ unsigned v_buf;
+ unsigned i_buf;
+ unsigned l_buf;
+} LANPR_PrivateData;
+
+typedef struct LANPR_StorageList {
+ LANPR_PrivateData *g_data;
+} LANPR_StorageList;
+
+typedef struct LANPR_BatchItem {
+ Link item;
+ GPUBatch *dpix_transform_batch;
+ GPUBatch *dpix_preview_batch;
+ Object *ob;
+} LANPR_BatchItem;
+
+typedef struct LANPR_Data {
+ void *engine_type;
+ LANPR_FramebufferList *fbl;
+ LANPR_TextureList *txl;
+ LANPR_PassList *psl;
+ LANPR_StorageList *stl;
+} LANPR_Data;
+
+/* functions */
+
+void lanpr_init_atlas_inputs(void *ved);
+void lanpr_destroy_atlas(void *ved);
+int lanpr_feed_atlas_data_obj(void *vedata,
+ float *AtlasPointsL,
+ float *AtlasPointsR,
+ float *AtlasFaceNormalL,
+ float *AtlasFaceNormalR,
+ float *AtlasEdgeMask,
+ const Object *ob,
+ const int begin_index);
+
+int lanpr_feed_atlas_data_intersection_cache(void *vedata,
+ float *AtlasPointsL,
+ float *AtlasPointsR,
+ float *AtlasFaceNormalL,
+ float *AtlasFaceNormalR,
+ float *AtlasEdgeMask,
+ const int begin_index);
+
+int lanpr_feed_atlas_trigger_preview_obj(void *vedata, Object *ob, const int begin_index);
+void lanpr_create_atlas_intersection_preview(void *vedata, const int begin_index);
+
+void lanpr_dpix_draw_scene(LANPR_TextureList *txl,
+ LANPR_FramebufferList *fbl,
+ LANPR_PassList *psl,
+ LANPR_PrivateData *pd,
+ SceneLANPR *lanpr,
+ GPUFrameBuffer *DefaultFB,
+ int is_render);
+
+void lanpr_software_draw_scene(void *vedata, GPUFrameBuffer *dfb, const int is_render);
+
+int lanpr_dpix_texture_size(const SceneLANPR *lanpr);
+
+void lanpr_chain_generate_draw_command(struct LANPR_RenderBuffer *rb);
+
+#endif
diff --git a/source/blender/draw/engines/lanpr/lanpr_chain_draw.c b/source/blender/draw/engines/lanpr/lanpr_chain_draw.c
new file mode 100644
index 00000000000..9ca94a806bd
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/lanpr_chain_draw.c
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ *
+ * Copyright 2019, Blender Foundation.
+ *
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "BKE_customdata.h"
+#include "BKE_object.h"
+
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_lanpr_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "GPU_batch.h"
+#include "GPU_draw.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_shader.h"
+#include "GPU_uniformbuffer.h"
+#include "GPU_viewport.h"
+
+#include "lanpr_all.h"
+#include "bmesh.h"
+
+#include <math.h>
+
+static float lanpr_compute_chain_length_draw(const LANPR_RenderLineChain *rlc,
+ float *lengths,
+ const int begin_index)
+{
+ LANPR_RenderLineChainItem *rlci;
+ int i = 0;
+ 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;
+ lengths[begin_index + i] = offset_accum;
+ copy_v2_v2(last_point, rlci->pos);
+ i++;
+ }
+ return offset_accum;
+}
+
+static int lanpr_get_gpu_line_type(const LANPR_RenderLineChainItem *rlci)
+{
+ switch (rlci->line_type) {
+ case LANPR_EDGE_FLAG_CONTOUR:
+ return 0;
+ case LANPR_EDGE_FLAG_CREASE:
+ return 1;
+ case LANPR_EDGE_FLAG_MATERIAL:
+ return 2;
+ case LANPR_EDGE_FLAG_EDGE_MARK:
+ return 3;
+ case LANPR_EDGE_FLAG_INTERSECTION:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+/** Endpoint flag,
+ * use an unreasonably big value in lanpr but within float range.
+ * This value is used to determin endpoint in the shader.
+ * Keep in sync with the one in lanpr_software_chain_geom.glsl */
+#define LANPR_CHAIN_ENDPOINT_FLAG 3e30f
+
+void lanpr_chain_generate_draw_command(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderLineChain *rlc;
+ LANPR_RenderLineChainItem *rlci;
+ int vert_count = 0;
+ int i = 0;
+ int arg;
+ float total_length;
+ float *lengths;
+ float length_target[2];
+
+ static GPUVertFormat format = {0};
+ static struct {
+ uint pos, uvs, normal, type, level;
+ } attr_id;
+ if (format.attr_len == 0) {
+ attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ attr_id.uvs = GPU_vertformat_attr_add(&format, "uvs", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ attr_id.normal = GPU_vertformat_attr_add(&format, "normal", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ attr_id.type = GPU_vertformat_attr_add(&format, "type", GPU_COMP_I32, 1, GPU_FETCH_INT);
+ attr_id.level = GPU_vertformat_attr_add(&format, "level", GPU_COMP_I32, 1, GPU_FETCH_INT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+
+ for (rlc = rb->chains.first; rlc; rlc = rlc->next) {
+ int count = ED_lanpr_count_chain(rlc);
+ /* printf("seg contains %d verts\n", count); */
+ vert_count += count;
+ }
+
+ GPU_vertbuf_data_alloc(vbo, vert_count + 1); /* serve as end point's adj. */
+
+ lengths = MEM_callocN(sizeof(float) * vert_count, "chain lengths");
+
+ GPUIndexBufBuilder elb;
+ /* We don't need a dummy vert for the end point. Thus no "+1" here */
+ GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINES_ADJ, vert_count * 4, vert_count);
+
+ for (rlc = rb->chains.first; rlc; rlc = rlc->next) {
+
+ total_length = lanpr_compute_chain_length_draw(rlc, lengths, i);
+
+ for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
+
+ length_target[0] = lengths[i];
+ length_target[1] = total_length - lengths[i];
+
+ GPU_vertbuf_attr_set(vbo, attr_id.pos, i, rlci->pos);
+ GPU_vertbuf_attr_set(vbo, attr_id.normal, i, rlci->normal);
+ GPU_vertbuf_attr_set(vbo, attr_id.uvs, i, length_target);
+
+ arg = lanpr_get_gpu_line_type(rlci);
+ GPU_vertbuf_attr_set(vbo, attr_id.type, i, &arg);
+
+ arg = (int)rlci->occlusion;
+ GPU_vertbuf_attr_set(vbo, attr_id.level, i, &arg);
+
+ if (rlci == rlc->chain.last) {
+ if (rlci->prev == rlc->chain.first) {
+ length_target[1] = total_length;
+ GPU_vertbuf_attr_set(vbo, attr_id.uvs, i, length_target);
+ }
+ i++;
+ continue;
+ }
+
+ if (rlci == rlc->chain.first) {
+ if (rlci->next == rlc->chain.last) {
+ GPU_indexbuf_add_line_adj_verts(&elb, vert_count - 1, i, i + 1, vert_count - 1);
+ }
+ else {
+ GPU_indexbuf_add_line_adj_verts(&elb, vert_count - 1, i, i + 1, i + 2);
+ }
+ }
+ else {
+ if (rlci->next == rlc->chain.last) {
+ GPU_indexbuf_add_line_adj_verts(&elb, i - 1, i, i + 1, vert_count - 1);
+ }
+ else {
+ GPU_indexbuf_add_line_adj_verts(&elb, i - 1, i, i + 1, i + 2);
+ }
+ }
+
+ i++;
+ }
+ }
+
+ /* set end point flag value. */
+ length_target[0] = LANPR_CHAIN_ENDPOINT_FLAG;
+ length_target[1] = LANPR_CHAIN_ENDPOINT_FLAG;
+ GPU_vertbuf_attr_set(vbo, attr_id.pos, vert_count, length_target);
+
+ MEM_freeN(lengths);
+
+ if (rb->chain_draw_batch) {
+ GPU_BATCH_DISCARD_SAFE(rb->chain_draw_batch);
+ }
+ rb->chain_draw_batch = GPU_batch_create_ex(GPU_PRIM_LINES_ADJ,
+ vbo,
+ GPU_indexbuf_build(&elb),
+ GPU_USAGE_DYNAMIC | GPU_BATCH_OWNS_VBO |
+ GPU_BATCH_OWNS_INDEX);
+}
+
+void ED_lanpr_render_buffer_cache_free(LANPR_RenderBuffer *rb)
+{
+ if (rb->chain_draw_batch) {
+ GPU_BATCH_DISCARD_SAFE(rb->chain_draw_batch);
+ }
+}
diff --git a/source/blender/draw/engines/lanpr/lanpr_cpu.c b/source/blender/draw/engines/lanpr/lanpr_cpu.c
new file mode 100644
index 00000000000..1010fa13fb8
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/lanpr_cpu.c
@@ -0,0 +1,520 @@
+/*
+ * 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.
+ *
+ * Copyright 2019, Blender Foundation.
+ *
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "DNA_camera_types.h"
+#include "DNA_lanpr_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_text_types.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "ED_lanpr.h"
+
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math_matrix.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_camera.h"
+#include "BKE_collection.h"
+#include "BKE_customdata.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_screen.h"
+#include "BKE_text.h"
+
+#include "GPU_batch.h"
+#include "GPU_draw.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_shader.h"
+#include "GPU_uniformbuffer.h"
+#include "GPU_viewport.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "bmesh.h"
+#include "bmesh_class.h"
+#include "bmesh_tools.h"
+
+#include "lanpr_all.h"
+
+#include <math.h>
+
+extern LANPR_SharedResource lanpr_share;
+extern const char *RE_engine_id_BLENDER_LANPR;
+
+static void lanpr_rebuild_render_draw_command(LANPR_RenderBuffer *rb, LANPR_LineLayer *ll)
+{
+ int count = 0;
+ float *v, *tv, *N, *tn;
+ int i;
+ int vert_count;
+
+ static GPUVertFormat format = {0};
+ static struct {
+ uint pos, normal;
+ } attr_id;
+ if (format.attr_len == 0) {
+ attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ attr_id.normal = GPU_vertformat_attr_add(&format, "normal", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+
+ if (ll->contour.use) {
+ count += ED_lanpr_count_leveled_edge_segment_count(&rb->contours, ll);
+ }
+ if (ll->crease.use) {
+ count += ED_lanpr_count_leveled_edge_segment_count(&rb->crease_lines, ll);
+ }
+ if (ll->intersection.use) {
+ count += ED_lanpr_count_leveled_edge_segment_count(&rb->intersection_lines, ll);
+ }
+ if (ll->edge_mark.use) {
+ count += ED_lanpr_count_leveled_edge_segment_count(&rb->edge_marks, ll);
+ }
+ if (ll->material_separate.use) {
+ count += ED_lanpr_count_leveled_edge_segment_count(&rb->material_lines, ll);
+ }
+
+ vert_count = count * 2;
+
+ if (!vert_count) {
+ return;
+ }
+
+ GPU_vertbuf_data_alloc(vbo, vert_count);
+
+ tv = v = MEM_callocN(sizeof(float) * 6 * count, "temp v data");
+ tn = N = MEM_callocN(sizeof(float) * 6 * count, "temp n data");
+
+ if (ll->contour.use) {
+ tv = ED_lanpr_make_leveled_edge_vertex_array(rb, &rb->contours, tv, tn, &tn, ll, 1.0f);
+ }
+ if (ll->crease.use) {
+ tv = ED_lanpr_make_leveled_edge_vertex_array(rb, &rb->crease_lines, tv, tn, &tn, ll, 2.0f);
+ }
+ if (ll->material_separate.use) {
+ tv = ED_lanpr_make_leveled_edge_vertex_array(rb, &rb->material_lines, tv, tn, &tn, ll, 3.0f);
+ }
+ if (ll->edge_mark.use) {
+ tv = ED_lanpr_make_leveled_edge_vertex_array(rb, &rb->edge_marks, tv, tn, &tn, ll, 4.0f);
+ }
+ if (ll->intersection.use) {
+ tv = ED_lanpr_make_leveled_edge_vertex_array(
+ rb, &rb->intersection_lines, tv, tn, &tn, ll, 5.0f);
+ }
+
+ for (i = 0; i < vert_count; i++) {
+ GPU_vertbuf_attr_set(vbo, attr_id.pos, i, &v[i * 3]);
+ GPU_vertbuf_attr_set(vbo, attr_id.normal, i, &N[i * 3]);
+ }
+
+ MEM_freeN(v);
+ MEM_freeN(N);
+
+ ll->batch = GPU_batch_create_ex(GPU_PRIM_LINES, vbo, 0, GPU_USAGE_DYNAMIC | GPU_BATCH_OWNS_VBO);
+}
+void ED_lanpr_rebuild_all_command(Scene *s)
+{
+ SceneLANPR *lanpr = &s->lanpr;
+ LANPR_LineLayer *ll;
+ if (!lanpr || !lanpr_share.render_buffer_shared) {
+ return;
+ }
+
+ if (lanpr->flags & LANPR_USE_CHAINING) {
+ lanpr_chain_generate_draw_command(lanpr_share.render_buffer_shared);
+ }
+ else {
+ for (ll = lanpr->line_layers.first; ll; ll = ll->next) {
+ if (ll->batch) {
+ GPU_BATCH_DISCARD_SAFE(ll->batch);
+ }
+ lanpr_rebuild_render_draw_command(lanpr_share.render_buffer_shared, ll);
+ }
+ }
+
+ DEG_id_tag_update(&s->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+void ED_lanpr_calculate_normal_object_vector(LANPR_LineLayer *ll, float *normal_object_direction)
+{
+ Object *ob;
+ if (!(ll->flags & LANPR_LINE_LAYER_NORMAL_ENABLED)) {
+ return;
+ }
+ switch (ll->normal_mode) {
+ case LANPR_NORMAL_DIRECTIONAL:
+ if (!(ob = ll->normal_control_object)) {
+ normal_object_direction[0] = 0;
+ normal_object_direction[1] = 0;
+ normal_object_direction[2] = 1; /* default z up direction */
+ }
+ else {
+ float dir[3] = {0, 0, 1};
+ float mat[3][3];
+ copy_m3_m4(mat, ob->obmat);
+ mul_v3_m3v3(normal_object_direction, mat, dir);
+ normalize_v3(normal_object_direction);
+ }
+ return;
+ case LANPR_NORMAL_POINT:
+ if (!(ob = ll->normal_control_object)) {
+ normal_object_direction[0] = 0;
+ normal_object_direction[1] = 0;
+ normal_object_direction[2] = 0; /* default origin position */
+ }
+ else {
+ normal_object_direction[0] = ob->obmat[3][0];
+ normal_object_direction[1] = ob->obmat[3][1];
+ normal_object_direction[2] = ob->obmat[3][2];
+ }
+ return;
+ }
+}
+
+void lanpr_software_draw_scene(void *vedata, GPUFrameBuffer *dfb, const int is_render)
+{
+ LANPR_LineLayer *ll;
+ LANPR_PassList *psl = ((LANPR_Data *)vedata)->psl;
+ LANPR_TextureList *txl = ((LANPR_Data *)vedata)->txl;
+ LANPR_StorageList *stl = ((LANPR_Data *)vedata)->stl;
+ LANPR_FramebufferList *fbl = ((LANPR_Data *)vedata)->fbl;
+ LANPR_PrivateData *pd = stl->g_data;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+ SceneLANPR *lanpr = &scene->lanpr;
+ View3D *v3d = draw_ctx->v3d;
+ float identity_mat[4][4], win_mat[4][4];
+ static float normal_object_direction[3] = {0, 0, 1};
+ float use_background_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ static float camdx, camdy, camzoom;
+
+ if (is_render) {
+ ED_lanpr_rebuild_all_command(scene);
+ }
+
+ float clear_depth = 1.0f;
+ uint clear_stencil = 0xFF;
+ eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT | GPU_COLOR_BIT;
+ static int zero_value = 0;
+
+ copy_v3_v3(use_background_color, &scene->world->horr);
+ use_background_color[3] = scene->r.alphamode ? 0.0f : 1.0f;
+
+ GPU_framebuffer_bind(fbl->software_ms);
+ GPU_framebuffer_clear(
+ fbl->software_ms, clear_bits, use_background_color, clear_depth, clear_stencil);
+
+ if (lanpr_share.render_buffer_shared) {
+
+ int texw = GPU_texture_width(txl->ms_resolve_color),
+ texh = GPU_texture_height(txl->ms_resolve_color);
+
+ pd->dpix_viewport[2] = texw;
+ pd->dpix_viewport[3] = texh;
+ if (is_render) {
+ pd->output_viewport[2] = scene->r.xsch;
+ pd->output_viewport[3] = scene->r.ysch;
+ }
+ else {
+ pd->output_viewport[2] = texw;
+ pd->output_viewport[3] = texh;
+ }
+
+ unit_m4(identity_mat);
+
+ DRW_view_winmat_get(NULL, win_mat, false);
+
+ DRWView *view = DRW_view_create(identity_mat, win_mat, NULL, NULL, NULL);
+ if (is_render) {
+ DRW_view_set_active(view);
+ }
+
+ RegionView3D *rv3d = v3d ? draw_ctx->rv3d : NULL;
+ if ((!is_render) && (rv3d && rv3d->view == RV3D_CAMOB)) {
+ camdx = rv3d->camdx;
+ camdy = rv3d->camdy;
+ camzoom = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
+ }
+ else {
+ camdx = camdy = 0.0f;
+ camzoom = 1.0f;
+ }
+
+ if ((lanpr->flags & LANPR_USE_CHAINING) &&
+ lanpr_share.render_buffer_shared->chain_draw_batch) {
+ for (ll = lanpr->line_layers.last; ll; ll = ll->prev) {
+ LANPR_RenderBuffer *rb;
+ psl->software_pass = DRW_pass_create("Software Render Preview",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
+ DRW_STATE_DEPTH_LESS_EQUAL);
+ rb = lanpr_share.render_buffer_shared;
+ rb->chain_shgrp = DRW_shgroup_create(lanpr_share.software_chaining_shader,
+ psl->software_pass);
+
+ ED_lanpr_calculate_normal_object_vector(ll, normal_object_direction);
+
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "camdx", &camdx, 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "camdy", &camdy, 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "camzoom", &camzoom, 1);
+
+ DRW_shgroup_uniform_vec4(rb->chain_shgrp,
+ "color_contour",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color :
+ ll->contour.color,
+ 1);
+ DRW_shgroup_uniform_vec4(rb->chain_shgrp,
+ "color_crease",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color :
+ ll->crease.color,
+ 1);
+ DRW_shgroup_uniform_vec4(rb->chain_shgrp,
+ "color_material",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ ll->color :
+ ll->material_separate.color,
+ 1);
+ DRW_shgroup_uniform_vec4(
+ rb->chain_shgrp,
+ "color_edge_mark",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->edge_mark.color,
+ 1);
+ DRW_shgroup_uniform_vec4(
+ rb->chain_shgrp,
+ "color_intersection",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->intersection.color,
+ 1);
+ static float unit_thickness = 1.0f;
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "thickness", &ll->thickness, 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp,
+ "thickness_contour",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &unit_thickness :
+ &ll->contour.thickness,
+ 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp,
+ "thickness_crease",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &unit_thickness :
+ &ll->crease.thickness,
+ 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp,
+ "thickness_material",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &unit_thickness :
+ &ll->material_separate.thickness,
+ 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp,
+ "thickness_edge_mark",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &unit_thickness :
+ &ll->edge_mark.thickness,
+ 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp,
+ "thickness_intersection",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &unit_thickness :
+ &ll->intersection.thickness,
+ 1);
+ DRW_shgroup_uniform_int(rb->chain_shgrp, "use_contour", &ll->contour.use, 1);
+ DRW_shgroup_uniform_int(rb->chain_shgrp, "use_crease", &ll->crease.use, 1);
+ DRW_shgroup_uniform_int(rb->chain_shgrp, "use_material", &ll->material_separate.use, 1);
+ DRW_shgroup_uniform_int(rb->chain_shgrp, "use_edge_mark", &ll->edge_mark.use, 1);
+ DRW_shgroup_uniform_int(rb->chain_shgrp, "use_intersection", &ll->intersection.use, 1);
+
+ static int normal_effect_inverse;
+ normal_effect_inverse = (ll->flags & LANPR_LINE_LAYER_NORMAL_INVERSE) ? 1 : 0;
+ DRW_shgroup_uniform_int(rb->chain_shgrp,
+ "normal_mode",
+ (ll->flags & LANPR_LINE_LAYER_NORMAL_ENABLED) ? &ll->normal_mode :
+ &zero_value,
+ 1);
+ DRW_shgroup_uniform_int(
+ rb->chain_shgrp, "normal_effect_inverse", &normal_effect_inverse, 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "normal_ramp_begin", &ll->normal_ramp_begin, 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "normal_ramp_end", &ll->normal_ramp_end, 1);
+ DRW_shgroup_uniform_float(
+ rb->chain_shgrp, "normal_thickness_start", &ll->normal_thickness_start, 1);
+ DRW_shgroup_uniform_float(
+ rb->chain_shgrp, "normal_thickness_end", &ll->normal_thickness_end, 1);
+ DRW_shgroup_uniform_vec3(rb->chain_shgrp, "normal_direction", normal_object_direction, 1);
+
+ DRW_shgroup_uniform_int(rb->chain_shgrp, "occlusion_level_start", &ll->level_start, 1);
+ DRW_shgroup_uniform_int(
+ rb->chain_shgrp,
+ "occlusion_level_end",
+ (ll->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS) ? &ll->level_end : &ll->level_start,
+ 1);
+
+ DRW_shgroup_uniform_vec4(
+ rb->chain_shgrp, "preview_viewport", stl->g_data->dpix_viewport, 1);
+ DRW_shgroup_uniform_vec4(
+ rb->chain_shgrp, "output_viewport", stl->g_data->output_viewport, 1);
+
+ float *tld = &lanpr->taper_left_distance, *tls = &lanpr->taper_left_strength,
+ *trd = &lanpr->taper_right_distance, *trs = &lanpr->taper_right_strength;
+
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "taper_l_dist", tld, 1);
+ DRW_shgroup_uniform_float(rb->chain_shgrp, "taper_l_strength", tls, 1);
+ DRW_shgroup_uniform_float(
+ rb->chain_shgrp, "taper_r_dist", (lanpr->flags & LANPR_SAME_TAPER) ? tld : trd, 1);
+ DRW_shgroup_uniform_float(
+ rb->chain_shgrp, "taper_r_strength", (lanpr->flags & LANPR_SAME_TAPER) ? tls : trs, 1);
+
+ /* need to add component enable/disable option. */
+ DRW_shgroup_call(
+ rb->chain_shgrp, lanpr_share.render_buffer_shared->chain_draw_batch, NULL);
+ /* debug purpose */
+ /* DRW_draw_pass(psl->color_pass); */
+ /* DRW_draw_pass(psl->color_pass); */
+ DRW_draw_pass(psl->software_pass);
+ }
+ }
+ else if (!(lanpr->flags & LANPR_USE_CHAINING)) {
+ for (ll = lanpr->line_layers.last; ll; ll = ll->prev) {
+ if (ll->batch) {
+ psl->software_pass = DRW_pass_create("Software Render Preview",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
+ DRW_STATE_DEPTH_LESS_EQUAL);
+ ll->shgrp = DRW_shgroup_create(lanpr_share.software_shader, psl->software_pass);
+
+ ED_lanpr_calculate_normal_object_vector(ll, normal_object_direction);
+
+ DRW_shgroup_uniform_float(ll->shgrp, "camdx", &camdx, 1);
+ DRW_shgroup_uniform_float(ll->shgrp, "camdy", &camdy, 1);
+ DRW_shgroup_uniform_float(ll->shgrp, "camzoom", &camzoom, 1);
+
+ DRW_shgroup_uniform_vec4(
+ ll->shgrp,
+ "color_contour",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->contour.color,
+ 1);
+ DRW_shgroup_uniform_vec4(
+ ll->shgrp,
+ "color_crease",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->crease.color,
+ 1);
+ DRW_shgroup_uniform_vec4(ll->shgrp,
+ "color_material",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ ll->color :
+ ll->material_separate.color,
+ 1);
+ DRW_shgroup_uniform_vec4(
+ ll->shgrp,
+ "color_edge_mark",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->edge_mark.color,
+ 1);
+ DRW_shgroup_uniform_vec4(
+ ll->shgrp,
+ "color_intersection",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->intersection.color,
+ 1);
+ static float uniform_thickness = 1.0f;
+ DRW_shgroup_uniform_float(ll->shgrp, "thickness", &ll->thickness, 1);
+ DRW_shgroup_uniform_float(ll->shgrp,
+ "thickness_contour",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &uniform_thickness :
+ &ll->contour.thickness,
+ 1);
+ DRW_shgroup_uniform_float(ll->shgrp,
+ "thickness_crease",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &uniform_thickness :
+ &ll->crease.thickness,
+ 1);
+ DRW_shgroup_uniform_float(ll->shgrp,
+ "thickness_material",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &uniform_thickness :
+ &ll->material_separate.thickness,
+ 1);
+ DRW_shgroup_uniform_float(ll->shgrp,
+ "thickness_edge_mark",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &uniform_thickness :
+ &ll->edge_mark.thickness,
+ 1);
+ DRW_shgroup_uniform_float(ll->shgrp,
+ "thickness_intersection",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &uniform_thickness :
+ &ll->intersection.thickness,
+ 1);
+ DRW_shgroup_uniform_vec4(ll->shgrp, "preview_viewport", stl->g_data->dpix_viewport, 1);
+ DRW_shgroup_uniform_vec4(ll->shgrp, "output_viewport", stl->g_data->output_viewport, 1);
+
+ static int normal_effect_inverse;
+ normal_effect_inverse = (ll->flags & LANPR_LINE_LAYER_NORMAL_INVERSE) ? 1 : 0;
+ DRW_shgroup_uniform_int(
+ ll->shgrp,
+ "normal_mode",
+ (ll->flags & LANPR_LINE_LAYER_NORMAL_ENABLED) ? &ll->normal_mode : &zero_value,
+ 1);
+ DRW_shgroup_uniform_int(ll->shgrp, "normal_effect_inverse", &normal_effect_inverse, 1);
+ DRW_shgroup_uniform_float(ll->shgrp, "normal_ramp_begin", &ll->normal_ramp_begin, 1);
+ DRW_shgroup_uniform_float(ll->shgrp, "normal_ramp_end", &ll->normal_ramp_end, 1);
+ DRW_shgroup_uniform_float(
+ ll->shgrp, "normal_thickness_start", &ll->normal_thickness_start, 1);
+ DRW_shgroup_uniform_float(
+ ll->shgrp, "normal_thickness_end", &ll->normal_thickness_end, 1);
+ DRW_shgroup_uniform_vec3(ll->shgrp, "normal_direction", normal_object_direction, 1);
+
+ DRW_shgroup_call(ll->shgrp, ll->batch, NULL);
+ DRW_draw_pass(psl->software_pass);
+ }
+ }
+ }
+ }
+
+ GPU_framebuffer_blit(fbl->software_ms, 0, dfb, 0, GPU_COLOR_BIT);
+
+ if (!is_render) {
+ DRW_view_set_active(NULL);
+ }
+}
+
+void ED_lanpr_update_render_progress(const char *text)
+{
+ if (lanpr_share.re_render) {
+ RE_engine_update_stats(lanpr_share.re_render, NULL, text);
+ }
+}
diff --git a/source/blender/draw/engines/lanpr/lanpr_dpix.c b/source/blender/draw/engines/lanpr/lanpr_dpix.c
new file mode 100644
index 00000000000..ddc7e4d38f7
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/lanpr_dpix.c
@@ -0,0 +1,558 @@
+/*
+ * 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.
+ *
+ * Copyright 2019, Blender Foundation.
+ *
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "BKE_customdata.h"
+#include "BKE_object.h"
+
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_lanpr_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "GPU_batch.h"
+#include "GPU_draw.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_shader.h"
+#include "GPU_uniformbuffer.h"
+#include "GPU_viewport.h"
+
+#include "bmesh.h"
+#include "lanpr_all.h"
+
+#include <math.h>
+
+extern LANPR_SharedResource lanpr_share;
+extern char datatoc_lanpr_dpix_project_passthrough_vert_glsl[];
+extern char datatoc_lanpr_dpix_project_clip_frag_glsl[];
+extern char datatoc_lanpr_dpix_preview_geom_glsl[];
+extern char datatoc_lanpr_dpix_preview_frag_glsl[];
+
+int lanpr_dpix_texture_size(const SceneLANPR *lanpr)
+{
+ switch (lanpr->gpu_cache_size) {
+ case LANPR_GPU_CACHE_SIZE_512:
+ return 512;
+ case LANPR_GPU_CACHE_SIZE_1K:
+ return 1024;
+ case LANPR_GPU_CACHE_SIZE_2K:
+ return 2048;
+ case LANPR_GPU_CACHE_SIZE_4K:
+ return 4096;
+ case LANPR_GPU_CACHE_SIZE_8K:
+ return 8192;
+ case LANPR_GPU_CACHE_SIZE_16K:
+ return 16384;
+ }
+ return 512;
+}
+
+void lanpr_init_atlas_inputs(void *ved)
+{
+ lanpr_share.ved_viewport = ved;
+ LANPR_Data *vedata = (LANPR_Data *)ved;
+ LANPR_TextureList *txl = vedata->txl;
+ LANPR_FramebufferList *fbl = vedata->fbl;
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ SceneLANPR *lanpr = &draw_ctx->scene->lanpr;
+
+ int texture_size = lanpr_dpix_texture_size(lanpr);
+ lanpr_share.texture_size = texture_size;
+
+ if (txl->dpix_in_pl && GPU_texture_width(txl->dpix_in_pl) != texture_size) {
+ DRW_texture_free(txl->dpix_in_pl);
+ txl->dpix_in_pl = NULL;
+ DRW_texture_free(txl->dpix_in_pr);
+ txl->dpix_in_pr = NULL;
+ DRW_texture_free(txl->dpix_in_nl);
+ txl->dpix_in_nl = NULL;
+ DRW_texture_free(txl->dpix_in_nr);
+ txl->dpix_in_nr = NULL;
+ DRW_texture_free(txl->dpix_out_pl);
+ txl->dpix_out_pl = NULL;
+ DRW_texture_free(txl->dpix_out_pr);
+ txl->dpix_out_pr = NULL;
+ DRW_texture_free(txl->dpix_out_length);
+ txl->dpix_out_length = NULL;
+ }
+
+ if (lanpr_share.dpix_reloaded || !txl->dpix_in_pl) {
+ DRW_texture_ensure_2d(&txl->dpix_in_pl, texture_size, texture_size, GPU_RGBA32F, 0);
+ DRW_texture_ensure_2d(&txl->dpix_in_pr, texture_size, texture_size, GPU_RGBA32F, 0);
+ DRW_texture_ensure_2d(&txl->dpix_in_nl, texture_size, texture_size, GPU_RGBA32F, 0);
+ DRW_texture_ensure_2d(&txl->dpix_in_nr, texture_size, texture_size, GPU_RGBA32F, 0);
+ DRW_texture_ensure_2d(&txl->dpix_in_edge_mask, texture_size, texture_size, GPU_RGBA8, 0);
+ DRW_texture_ensure_2d(&txl->dpix_out_pl, texture_size, texture_size, GPU_RGBA32F, 0);
+ DRW_texture_ensure_2d(&txl->dpix_out_pr, texture_size, texture_size, GPU_RGBA32F, 0);
+ DRW_texture_ensure_2d(&txl->dpix_out_length, texture_size, texture_size, GPU_RGBA32F, 0);
+ }
+
+ GPU_framebuffer_ensure_config(&fbl->dpix_transform,
+ {GPU_ATTACHMENT_LEAVE,
+ GPU_ATTACHMENT_TEXTURE(txl->dpix_out_pl),
+ GPU_ATTACHMENT_TEXTURE(txl->dpix_out_pr),
+ GPU_ATTACHMENT_TEXTURE(txl->dpix_out_length),
+ GPU_ATTACHMENT_LEAVE,
+ GPU_ATTACHMENT_LEAVE,
+ GPU_ATTACHMENT_LEAVE});
+
+ GPU_framebuffer_ensure_config(&fbl->dpix_preview,
+ {GPU_ATTACHMENT_TEXTURE(txl->depth),
+ GPU_ATTACHMENT_TEXTURE(txl->color),
+ GPU_ATTACHMENT_LEAVE,
+ GPU_ATTACHMENT_LEAVE,
+ GPU_ATTACHMENT_LEAVE,
+ GPU_ATTACHMENT_LEAVE,
+ GPU_ATTACHMENT_LEAVE});
+
+ if (lanpr_share.dpix_transform_shader == NULL) {
+ lanpr_share.dpix_transform_shader = DRW_shader_create(
+ datatoc_lanpr_dpix_project_passthrough_vert_glsl,
+ NULL,
+ datatoc_lanpr_dpix_project_clip_frag_glsl,
+ NULL);
+ if (lanpr_share.dpix_transform_shader == NULL) {
+ lanpr_share.dpix_shader_error = true;
+ printf("LANPR: DPIX transform shader compile error.");
+ }
+ }
+ if (lanpr_share.dpix_preview_shader == NULL) {
+ lanpr_share.dpix_preview_shader = DRW_shader_create(
+ datatoc_lanpr_dpix_project_passthrough_vert_glsl,
+ datatoc_lanpr_dpix_preview_geom_glsl,
+ datatoc_lanpr_dpix_preview_frag_glsl,
+ NULL);
+ if (lanpr_share.dpix_transform_shader == NULL) {
+ lanpr_share.dpix_shader_error = true;
+ printf("LANPR: DPIX transform shader compile error.");
+ }
+ }
+}
+void lanpr_destroy_atlas(void *UNUSED(ved))
+{
+ /* no need to free things, no custom data. */
+}
+
+int lanpr_feed_atlas_data_obj(void *UNUSED(vedata),
+ float *AtlasPointsL,
+ float *AtlasPointsR,
+ float *AtlasFaceNormalL,
+ float *AtlasFaceNormalR,
+ float *AtlasEdgeMask,
+ const Object *ob,
+ const int begin_index)
+{
+ if (!DRW_object_is_renderable(ob)) {
+ return begin_index;
+ }
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ if (ob == draw_ctx->object_edit) {
+ return begin_index;
+ }
+ if (ob->type != OB_MESH) {
+ return begin_index;
+ }
+
+ Mesh *me = ob->data;
+ BMesh *bm;
+ struct BMFace *f1, *f2;
+ struct BMVert *v1, *v2;
+ struct BMEdge *e;
+ struct BMLoop *l1, *l2;
+ FreestyleEdge *fe;
+ int CanFindFreestyle = 0;
+ int edge_count = me->totedge;
+ int i, idx;
+
+ int cache_total = lanpr_share.texture_size * lanpr_share.texture_size;
+
+ /* Don't overflow the cache. */
+ if ((edge_count + begin_index) > (cache_total - 1)) {
+ WM_report(RPT_WARNING, "LANPR: GPU Cache too small for displaying some of the objects.");
+ return begin_index;
+ }
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me);
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+ BM_mesh_bm_from_me(bm,
+ me,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
+ CanFindFreestyle = 1;
+ }
+
+ for (i = 0; i < edge_count; i++) {
+ f1 = 0;
+ f2 = 0;
+ e = BM_edge_at_index(bm, i);
+ v1 = e->v1;
+ v2 = e->v2;
+ l1 = e->l;
+ l2 = e->l ? e->l->radial_next : 0;
+ if (l1) {
+ f1 = l1->f;
+ }
+ if (l2) {
+ f2 = l2->f;
+ }
+
+ idx = (begin_index + i) * 4;
+
+ AtlasPointsL[idx + 0] = v1->co[0];
+ AtlasPointsL[idx + 1] = v1->co[1];
+ AtlasPointsL[idx + 2] = v1->co[2];
+ AtlasPointsL[idx + 3] = 1;
+
+ AtlasPointsR[idx + 0] = v2->co[0];
+ AtlasPointsR[idx + 1] = v2->co[1];
+ AtlasPointsR[idx + 2] = v2->co[2];
+ AtlasPointsR[idx + 3] = 1;
+
+ if (CanFindFreestyle) {
+ fe = CustomData_bmesh_get(&bm->edata, e->head.data, FREESTYLE_EDGE_MARK);
+ if (fe->flag & FREESTYLE_EDGE_MARK) {
+ AtlasEdgeMask[idx + 1] = 1; /* channel G */
+ }
+ }
+
+ if (f1) {
+ AtlasFaceNormalL[idx + 0] = f1->no[0];
+ AtlasFaceNormalL[idx + 1] = f1->no[1];
+ AtlasFaceNormalL[idx + 2] = f1->no[2];
+ AtlasFaceNormalL[idx + 3] = 1;
+ }
+ else {
+ AtlasFaceNormalL[idx + 0] = 0;
+ AtlasFaceNormalL[idx + 1] = 0;
+ AtlasFaceNormalL[idx + 2] = 0;
+ AtlasFaceNormalL[idx + 3] = 0;
+ }
+
+ if (f2 && f2 != f1) { /* this is for edge condition */
+ AtlasFaceNormalR[idx + 0] = f2->no[0];
+ AtlasFaceNormalR[idx + 1] = f2->no[1];
+ AtlasFaceNormalR[idx + 2] = f2->no[2];
+ AtlasFaceNormalR[idx + 3] = 1;
+
+ if (f2->mat_nr != f1->mat_nr) {
+ AtlasEdgeMask[idx] = 1; /* channel r */
+ }
+ }
+ else {
+ AtlasFaceNormalR[idx + 0] = 0;
+ AtlasFaceNormalR[idx + 1] = 0;
+ AtlasFaceNormalR[idx + 2] = 0;
+ AtlasFaceNormalR[idx + 3] = 0;
+ }
+ }
+
+ BM_mesh_free(bm);
+
+ return begin_index + edge_count;
+}
+
+int lanpr_feed_atlas_data_intersection_cache(void *UNUSED(vedata),
+ float *AtlasPointsL,
+ float *AtlasPointsR,
+ float *AtlasFaceNormalL,
+ float *AtlasFaceNormalR,
+ float *AtlasEdgeMask,
+ const int begin_index)
+{
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+ LinkData *lip;
+ LANPR_RenderLine *rl;
+ int i, idx;
+
+ i = 0;
+
+ if (rb == NULL) {
+ return 0;
+ }
+
+ int cache_total = lanpr_share.texture_size * lanpr_share.texture_size;
+
+ /* Don't overflow the cache. */
+ if ((rb->intersection_count + begin_index) > (cache_total - 1)) {
+ WM_report(RPT_WARNING, "LANPR: GPU Cache too small for displaying intersections.");
+ return 0;
+ }
+
+ for (lip = rb->intersection_lines.first; lip; lip = lip->next) {
+ rl = lip->data;
+
+ idx = (begin_index + i) * 4;
+ AtlasEdgeMask[idx + 2] = 1; /* channel B */
+
+ AtlasPointsL[idx + 0] = rl->l->gloc[0];
+ AtlasPointsL[idx + 1] = rl->l->gloc[1];
+ AtlasPointsL[idx + 2] = rl->l->gloc[2];
+ AtlasPointsL[idx + 3] = 1;
+
+ AtlasPointsR[idx + 0] = rl->r->gloc[0];
+ AtlasPointsR[idx + 1] = rl->r->gloc[1];
+ AtlasPointsR[idx + 2] = rl->r->gloc[2];
+ AtlasPointsR[idx + 3] = 1;
+
+ AtlasFaceNormalL[idx + 0] = 0;
+ AtlasFaceNormalL[idx + 1] = 0;
+ AtlasFaceNormalL[idx + 2] = 1;
+ AtlasFaceNormalL[idx + 3] = 0;
+
+ AtlasFaceNormalR[idx + 0] = 0;
+ AtlasFaceNormalR[idx + 1] = 0;
+ AtlasFaceNormalR[idx + 2] = 1;
+ AtlasFaceNormalR[idx + 3] = 0;
+
+ i++;
+ }
+
+ return begin_index + i;
+}
+
+static void lanpr_dpix_index_to_coord(const int index, float *x, float *y)
+{
+ int texture_size = lanpr_share.texture_size;
+ (*x) = interpf(1, -1, (float)(index % texture_size + 0.5) / (float)texture_size);
+ (*y) = interpf(1, -1, (float)(index / texture_size + 0.5) / (float)texture_size);
+}
+
+static void lanpr_dpix_index_to_coord_absolute(int index, float *x, float *y)
+{
+ int texture_size = lanpr_share.texture_size;
+ (*x) = (float)(index % texture_size) + 0.5;
+ (*y) = (float)(index / texture_size) + 0.5;
+}
+
+int lanpr_feed_atlas_trigger_preview_obj(void *UNUSED(vedata), Object *ob, const int begin_index)
+{
+ Mesh *me = ob->data;
+ if (ob->type != OB_MESH) {
+ return begin_index;
+ }
+ int edge_count = me->totedge;
+ int i;
+ float co[2];
+
+ int cache_total = lanpr_share.texture_size * lanpr_share.texture_size;
+
+ /* Don't overflow the cache. */
+ if ((edge_count + begin_index) > (cache_total - 1)) {
+ return begin_index;
+ }
+
+ static GPUVertFormat format = {0};
+ static struct {
+ uint pos, uvs;
+ } attr_id;
+ if (format.attr_len == 0) {
+ attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ static GPUVertFormat format2 = {0};
+ static struct {
+ uint pos, uvs;
+ } attr_id2;
+ if (format2.attr_len == 0) {
+ attr_id2.pos = GPU_vertformat_attr_add(&format2, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPUVertBuf *vbo2 = GPU_vertbuf_create_with_format(&format2);
+ GPU_vertbuf_data_alloc(vbo, edge_count);
+ GPU_vertbuf_data_alloc(vbo2, edge_count);
+
+ for (i = 0; i < edge_count; i++) {
+ lanpr_dpix_index_to_coord(i + begin_index, &co[0], &co[1]);
+ GPU_vertbuf_attr_set(vbo, attr_id.pos, i, co);
+ lanpr_dpix_index_to_coord_absolute(i + begin_index, &co[0], &co[1]);
+ GPU_vertbuf_attr_set(vbo2, attr_id2.pos, i, co);
+ }
+
+ GPUBatch *gb = GPU_batch_create_ex(
+ GPU_PRIM_POINTS, vbo, 0, GPU_USAGE_DYNAMIC | GPU_BATCH_OWNS_VBO);
+ GPUBatch *gb2 = GPU_batch_create_ex(
+ GPU_PRIM_POINTS, vbo2, 0, GPU_USAGE_DYNAMIC | GPU_BATCH_OWNS_VBO);
+
+ LANPR_BatchItem *bi = BLI_mempool_alloc(lanpr_share.mp_batch_list);
+ BLI_addtail(&lanpr_share.dpix_batch_list, bi);
+ bi->dpix_transform_batch = gb;
+ bi->dpix_preview_batch = gb2;
+ bi->ob = ob;
+
+ return begin_index + edge_count;
+}
+
+void lanpr_create_atlas_intersection_preview(void *UNUSED(vedata), const int begin_index)
+{
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+ float co[2];
+ int i;
+
+ if (rb == NULL) {
+ return;
+ }
+
+ if (rb->DPIXIntersectionBatch) {
+ GPU_BATCH_DISCARD_SAFE(rb->DPIXIntersectionBatch);
+ }
+ rb->DPIXIntersectionBatch = 0;
+
+ if (!rb->intersection_count) {
+ return;
+ }
+
+ int cache_total = lanpr_share.texture_size * lanpr_share.texture_size;
+
+ /* Don't overflow the cache. */
+ if ((rb->intersection_count + begin_index) > (cache_total - 1)) {
+ return;
+ }
+
+ static GPUVertFormat format = {0};
+ static struct {
+ uint pos, uvs;
+ } attr_id;
+ if (format.attr_len == 0) {
+ attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+ static GPUVertFormat format2 = {0};
+ static struct {
+ uint pos, uvs;
+ } attr_id2;
+ if (format2.attr_len == 0) {
+ attr_id2.pos = GPU_vertformat_attr_add(&format2, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, rb->intersection_count);
+
+ GPUVertBuf *vbo2 = GPU_vertbuf_create_with_format(&format2);
+ GPU_vertbuf_data_alloc(vbo2, rb->intersection_count);
+
+ for (i = 0; i < rb->intersection_count; i++) {
+ lanpr_dpix_index_to_coord(i + begin_index, &co[0], &co[1]);
+ GPU_vertbuf_attr_set(vbo, attr_id.pos, i, co);
+ lanpr_dpix_index_to_coord_absolute(i + begin_index, &co[0], &co[1]);
+ GPU_vertbuf_attr_set(vbo2, attr_id2.pos, i, co);
+ }
+ rb->DPIXIntersectionTransformBatch = GPU_batch_create_ex(
+ GPU_PRIM_POINTS, vbo, 0, GPU_USAGE_DYNAMIC | GPU_BATCH_OWNS_VBO);
+ rb->DPIXIntersectionBatch = GPU_batch_create_ex(
+ GPU_PRIM_POINTS, vbo2, 0, GPU_USAGE_DYNAMIC | GPU_BATCH_OWNS_VBO);
+}
+
+void lanpr_dpix_draw_scene(LANPR_TextureList *txl,
+ LANPR_FramebufferList *fbl,
+ LANPR_PassList *psl,
+ LANPR_PrivateData *pd,
+ SceneLANPR *lanpr,
+ GPUFrameBuffer *DefaultFB,
+ int is_render)
+{
+ float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float clear_depth = 1.0f;
+ uint clear_stencil = 0xFF;
+ int is_persp = 1;
+ float use_background_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ ;
+
+ if (lanpr->active_layer == NULL) {
+ return; /* return early in case we don't have line layers. DPIX only use the first layer. */
+ }
+ int texw = GPU_texture_width(txl->edge_intermediate),
+ texh = GPU_texture_height(txl->edge_intermediate);
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+ View3D *v3d = draw_ctx->v3d;
+ Object *camera = 0;
+ if (v3d) {
+ RegionView3D *rv3d = draw_ctx->rv3d;
+ camera = (rv3d && rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL;
+ is_persp = rv3d->is_persp;
+ }
+ if (camera == NULL) {
+ camera = scene->camera;
+ if (v3d == NULL) {
+ is_persp = ((Camera *)camera->data)->type == CAM_PERSP ? 1 : 0;
+ }
+ }
+ if (is_render && !camera) {
+ return;
+ }
+
+ int texture_size = lanpr_share.texture_size;
+
+ pd->dpix_viewport[2] = texw;
+ pd->dpix_viewport[3] = texh;
+ pd->dpix_is_perspective = is_persp;
+ pd->dpix_sample_step = 1;
+ pd->dpix_buffer_width = texture_size;
+ pd->dpix_depth_offset = 0.0001; /* This value works okay with default camera. */
+ pd->dpix_znear = camera ? ((Camera *)camera->data)->clip_start : v3d->clip_start;
+ pd->dpix_zfar = camera ? ((Camera *)camera->data)->clip_end : v3d->clip_end;
+
+ copy_v3_v3(use_background_color, &scene->world->horr);
+ use_background_color[3] = scene->r.alphamode ? 0.0f : 1.0f;
+
+ GPU_point_size(1);
+ /* GPU_line_width(2); */
+ GPU_framebuffer_bind(fbl->dpix_transform);
+ DRW_draw_pass(psl->dpix_transform_pass);
+
+ GPU_framebuffer_bind(fbl->dpix_preview);
+ eGPUFrameBufferBits clear_bits = GPU_COLOR_BIT;
+ GPU_framebuffer_clear(fbl->dpix_preview, clear_bits, clear_col, clear_depth, clear_stencil);
+ DRW_draw_pass(psl->dpix_preview_pass);
+
+ if (is_render) {
+ mul_v3_v3fl(clear_col, use_background_color, use_background_color[3]);
+ clear_col[3] = use_background_color[3];
+ }
+ else {
+ copy_v4_v4(clear_col, use_background_color);
+ }
+
+ GPU_framebuffer_bind(DefaultFB);
+ GPU_framebuffer_clear(DefaultFB, clear_bits, clear_col, clear_depth, clear_stencil);
+ // DRW_multisamples_resolve(txl->depth, txl->color, 0);
+}
diff --git a/source/blender/draw/engines/lanpr/lanpr_engine.c b/source/blender/draw/engines/lanpr/lanpr_engine.c
new file mode 100644
index 00000000000..5230ac201cb
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/lanpr_engine.c
@@ -0,0 +1,799 @@
+/*
+ * 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.
+ *
+ * Copyright 2019, Blender Foundation.
+ *
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math_matrix.h"
+#include "BLI_rect.h"
+#include "BLI_task.h"
+
+#include "BKE_object.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_lanpr_types.h"
+#include "DNA_mesh_types.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "GPU_batch.h"
+#include "GPU_draw.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_shader.h"
+#include "GPU_uniformbuffer.h"
+#include "GPU_viewport.h"
+
+#include "RE_pipeline.h"
+
+#include "bmesh.h"
+#include "lanpr_all.h"
+
+#include <math.h>
+
+extern char datatoc_common_fullscreen_vert_glsl[];
+extern char datatoc_gpu_shader_3D_smooth_color_vert_glsl[];
+extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[];
+extern char datatoc_lanpr_software_line_chain_geom_glsl[];
+extern char datatoc_lanpr_software_chain_geom_glsl[];
+extern char datatoc_lanpr_dpix_project_passthrough_vert_glsl[];
+extern char datatoc_lanpr_dpix_project_clip_frag_glsl[];
+extern char datatoc_lanpr_dpix_preview_frag_glsl[];
+extern char datatoc_lanpr_software_passthrough_vert_glsl[];
+extern char datatoc_gpu_shader_2D_smooth_color_vert_glsl[];
+extern char datatoc_gpu_shader_2D_smooth_color_frag_glsl[];
+extern char datatoc_gpu_shader_colorspace_lib_glsl[];
+
+LANPR_SharedResource lanpr_share;
+
+static void lanpr_engine_init(void *ved)
+{
+ lanpr_share.ved_viewport = ved;
+ LANPR_Data *vedata = (LANPR_Data *)ved;
+ LANPR_TextureList *txl = vedata->txl;
+ LANPR_FramebufferList *fbl = vedata->fbl;
+
+ if (!lanpr_share.init_complete) {
+ BLI_spin_init(&lanpr_share.lock_render_status);
+ }
+
+ DRW_texture_ensure_fullscreen_2d(&txl->depth, GPU_DEPTH_COMPONENT32F, 0);
+ DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA32F, 0);
+ DRW_texture_ensure_fullscreen_2d(&txl->normal, GPU_RGBA32F, 0);
+ DRW_texture_ensure_fullscreen_2d(&txl->edge_intermediate, GPU_RGBA32F, 0);
+
+ DRW_texture_ensure_fullscreen_2d(&txl->ms_resolve_depth, GPU_DEPTH_COMPONENT32F, 0);
+ DRW_texture_ensure_fullscreen_2d(&txl->ms_resolve_color, GPU_RGBA32F, 0);
+
+ GPU_framebuffer_ensure_config(&fbl->passes,
+ {GPU_ATTACHMENT_TEXTURE(txl->depth),
+ GPU_ATTACHMENT_TEXTURE(txl->color),
+ GPU_ATTACHMENT_TEXTURE(txl->normal)});
+
+ GPU_framebuffer_ensure_config(
+ &fbl->edge_intermediate,
+ {GPU_ATTACHMENT_TEXTURE(txl->depth), GPU_ATTACHMENT_TEXTURE(txl->edge_intermediate)});
+
+ if (lanpr_share.multichannel_shader == NULL) {
+ lanpr_share.multichannel_shader = GPU_shader_create_from_arrays({
+ .vert = (const char *[]){datatoc_gpu_shader_3D_smooth_color_vert_glsl, NULL},
+ .geom = (const char *[]){NULL},
+ .frag = (const char *[]){datatoc_gpu_shader_colorspace_lib_glsl,
+ datatoc_gpu_shader_3D_smooth_color_frag_glsl,
+ NULL},
+ .defs = (const char *[]){NULL},
+ });
+ }
+
+ /* DPIX */
+ lanpr_init_atlas_inputs(ved);
+
+ /* SOFTWARE */
+ if (lanpr_share.software_shader == NULL) {
+ lanpr_share.software_shader = DRW_shader_create(datatoc_lanpr_software_passthrough_vert_glsl,
+ datatoc_lanpr_software_line_chain_geom_glsl,
+ datatoc_lanpr_dpix_preview_frag_glsl,
+ NULL);
+ }
+
+ if (lanpr_share.software_chaining_shader == NULL) {
+ lanpr_share.software_chaining_shader = DRW_shader_create(
+ datatoc_lanpr_software_passthrough_vert_glsl,
+ datatoc_lanpr_software_chain_geom_glsl,
+ datatoc_lanpr_dpix_preview_frag_glsl,
+ NULL);
+ }
+
+ GPU_framebuffer_ensure_config(&fbl->software_ms,
+ {GPU_ATTACHMENT_TEXTURE(txl->ms_resolve_depth),
+ GPU_ATTACHMENT_TEXTURE(txl->ms_resolve_color)});
+
+ if (!(lanpr_share.init_complete & LANPR_INIT_LOCKS)) {
+ BLI_spin_init(&lanpr_share.lock_loader);
+ BLI_spin_init(&lanpr_share.lock_render_status);
+ lanpr_share.init_complete |= LANPR_INIT_LOCKS;
+ }
+
+ lanpr_share.init_complete |= LANPR_INIT_ENGINE;
+}
+
+void DRW_scene_lanpr_freecache(Scene *sce)
+{
+ LANPR_LineLayer *ll;
+
+ for (ll = sce->lanpr.line_layers.first; ll; ll = ll->next) {
+ if (ll->batch) {
+ GPU_batch_discard(ll->batch);
+ ll->batch = NULL;
+ }
+ }
+}
+
+static void lanpr_dpix_batch_free(void)
+{
+ LANPR_BatchItem *dpbi;
+ while ((dpbi = BLI_pophead(&lanpr_share.dpix_batch_list)) != NULL) {
+ GPU_BATCH_DISCARD_SAFE(dpbi->dpix_preview_batch);
+ GPU_BATCH_DISCARD_SAFE(dpbi->dpix_transform_batch);
+ }
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+ if (rb) {
+ if (rb->DPIXIntersectionBatch) {
+ GPU_BATCH_DISCARD_SAFE(rb->DPIXIntersectionBatch);
+ }
+ if (rb->DPIXIntersectionTransformBatch) {
+ GPU_BATCH_DISCARD_SAFE(rb->DPIXIntersectionTransformBatch);
+ }
+ }
+}
+
+static void lanpr_chain_batch_free(void)
+{
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+ if (rb) {
+ if (rb->chain_draw_batch) {
+ GPU_BATCH_DISCARD_SAFE(rb->DPIXIntersectionBatch);
+ }
+ }
+}
+
+static void lanpr_engine_free(void)
+{
+ DRW_SHADER_FREE_SAFE(lanpr_share.multichannel_shader);
+ DRW_SHADER_FREE_SAFE(lanpr_share.software_chaining_shader);
+ DRW_SHADER_FREE_SAFE(lanpr_share.dpix_preview_shader);
+ DRW_SHADER_FREE_SAFE(lanpr_share.dpix_transform_shader);
+ DRW_SHADER_FREE_SAFE(lanpr_share.software_shader);
+
+ lanpr_dpix_batch_free();
+ lanpr_chain_batch_free();
+
+ BLI_spin_end(&lanpr_share.lock_loader);
+ BLI_spin_end(&lanpr_share.lock_render_status);
+
+ if (lanpr_share.render_buffer_shared) {
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+ ED_lanpr_destroy_render_data(rb);
+
+ if (lanpr_share.background_render_task) {
+ BLI_task_pool_free(lanpr_share.background_render_task);
+ }
+
+ GPU_BATCH_DISCARD_SAFE(rb->chain_draw_batch);
+
+ MEM_freeN(rb);
+ lanpr_share.render_buffer_shared = NULL;
+ }
+
+ BLI_mempool *mp = lanpr_share.mp_batch_list;
+
+ if (mp) {
+ BLI_mempool_destroy(mp);
+ }
+}
+
+static void lanpr_cache_init(void *vedata)
+{
+
+ LANPR_PassList *psl = ((LANPR_Data *)vedata)->psl;
+ LANPR_StorageList *stl = ((LANPR_Data *)vedata)->stl;
+ LANPR_TextureList *txl = ((LANPR_Data *)vedata)->txl;
+
+ static float normal_object_direction[3] = {0, 0, 1};
+
+ /* Transfer reload state */
+ lanpr_share.dpix_reloaded = lanpr_share.dpix_reloaded_deg;
+
+ if (stl->g_data == NULL) {
+ /* Alloc transient pointers */
+ stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
+ }
+
+ if (lanpr_share.mp_batch_list == NULL) {
+ lanpr_share.mp_batch_list = BLI_mempool_create(
+ sizeof(LANPR_BatchItem), 0, 128, BLI_MEMPOOL_NOP);
+ }
+
+ LANPR_PrivateData *pd = stl->g_data;
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+ SceneLANPR *lanpr = &scene->lanpr;
+ View3D *v3d = draw_ctx->v3d;
+ RegionView3D *rv3d = v3d ? draw_ctx->rv3d : NULL;
+
+ bool is_render = (lanpr_share.viewport_camera_override < 0);
+
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+ if (rv3d && lanpr_share.viewport_camera_override >= 0) {
+ copy_v3_v3(lanpr_share.camera_pos, rv3d->viewinv[3]);
+ copy_m4_m4(lanpr_share.viewinv, rv3d->viewinv);
+ copy_m4_m4(lanpr_share.persp, rv3d->persmat);
+ copy_v4_v4(lanpr_share.viewquat, rv3d->viewquat);
+ lanpr_share.near_clip = v3d->clip_start;
+ lanpr_share.far_clip = v3d->clip_end;
+ lanpr_share.viewport_camera_override = 1;
+ lanpr_share.camera_is_persp = rv3d->is_persp;
+ }
+ else {
+ lanpr_share.viewport_camera_override = 0;
+ }
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+
+ int texture_size = lanpr_dpix_texture_size(lanpr);
+ lanpr_share.texture_size = texture_size;
+
+ psl->color_pass = DRW_pass_create(
+ "color Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WRITE_DEPTH);
+ stl->g_data->multipass_shgrp = DRW_shgroup_create(lanpr_share.multichannel_shader,
+ psl->color_pass);
+
+ if (lanpr->master_mode == LANPR_MASTER_MODE_DPIX && lanpr->active_layer &&
+ !lanpr_share.dpix_shader_error) {
+ LANPR_LineLayer *ll = lanpr->line_layers.first;
+ psl->dpix_transform_pass = DRW_pass_create("DPIX Transform Stage", DRW_STATE_WRITE_COLOR);
+ stl->g_data->dpix_transform_shgrp = DRW_shgroup_create(lanpr_share.dpix_transform_shader,
+ psl->dpix_transform_pass);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_transform_shgrp, "tex_vert0", &txl->dpix_in_pl);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_transform_shgrp, "tex_vert1", &txl->dpix_in_pr);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_transform_shgrp, "tex_fnormal0", &txl->dpix_in_nl);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_transform_shgrp, "tex_fnormal1", &txl->dpix_in_nr);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_transform_shgrp, "tex_edge_mask", &txl->dpix_in_edge_mask);
+ DRW_shgroup_uniform_int(
+ stl->g_data->dpix_transform_shgrp, "sample_step", &stl->g_data->dpix_sample_step, 1);
+ DRW_shgroup_uniform_int(
+ stl->g_data->dpix_transform_shgrp, "is_perspective", &stl->g_data->dpix_is_perspective, 1);
+ DRW_shgroup_uniform_vec4(
+ stl->g_data->dpix_transform_shgrp, "viewport", stl->g_data->dpix_viewport, 1);
+ DRW_shgroup_uniform_int(
+ stl->g_data->dpix_transform_shgrp, "buffer_width", &stl->g_data->dpix_buffer_width, 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_transform_shgrp, "crease_threshold", &lanpr->crease_threshold, 1);
+ DRW_shgroup_uniform_float(stl->g_data->dpix_transform_shgrp,
+ "crease_fade_threshold",
+ &lanpr->crease_fade_threshold,
+ 1);
+ DRW_shgroup_uniform_int(stl->g_data->dpix_transform_shgrp, "use_contour", &ll->contour.use, 1);
+ DRW_shgroup_uniform_int(stl->g_data->dpix_transform_shgrp, "use_crease", &ll->crease.use, 1);
+ DRW_shgroup_uniform_int(
+ stl->g_data->dpix_transform_shgrp, "use_material", &ll->material_separate.use, 1);
+ DRW_shgroup_uniform_int(
+ stl->g_data->dpix_transform_shgrp, "use_edge_mark", &ll->edge_mark.use, 1);
+ DRW_shgroup_uniform_int(
+ stl->g_data->dpix_transform_shgrp, "use_intersection", &ll->intersection.use, 1);
+
+ psl->dpix_preview_pass = DRW_pass_create("DPIX Preview",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
+ DRW_STATE_DEPTH_LESS_EQUAL);
+ stl->g_data->dpix_preview_shgrp = DRW_shgroup_create(lanpr_share.dpix_preview_shader,
+ psl->dpix_preview_pass);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_preview_shgrp, "tex_vert0", &txl->dpix_out_pl);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_preview_shgrp, "tex_vert1", &txl->dpix_out_pr);
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_preview_shgrp, "tex_fnormal0", &txl->dpix_in_nl);
+ DRW_shgroup_uniform_texture_ref(stl->g_data->dpix_preview_shgrp,
+ "tex_fnormal1",
+ &txl->dpix_in_nr); /* these are for normal shading */
+ DRW_shgroup_uniform_texture_ref(
+ stl->g_data->dpix_preview_shgrp, "tex_edge_mask", &txl->dpix_in_edge_mask);
+ DRW_shgroup_uniform_vec4(
+ stl->g_data->dpix_preview_shgrp, "viewport", stl->g_data->dpix_viewport, 1);
+ DRW_shgroup_uniform_vec4(stl->g_data->dpix_preview_shgrp,
+ "color_contour",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color :
+ ll->contour.color,
+ 1);
+ DRW_shgroup_uniform_vec4(stl->g_data->dpix_preview_shgrp,
+ "color_crease",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color :
+ ll->crease.color,
+ 1);
+ DRW_shgroup_uniform_vec4(
+ stl->g_data->dpix_preview_shgrp,
+ "color_material",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->material_separate.color,
+ 1);
+ DRW_shgroup_uniform_vec4(stl->g_data->dpix_preview_shgrp,
+ "color_edge_mark",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color :
+ ll->edge_mark.color,
+ 1);
+ DRW_shgroup_uniform_vec4(
+ stl->g_data->dpix_preview_shgrp,
+ "color_intersection",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? ll->color : ll->intersection.color,
+ 1);
+ static float use_background_color[4];
+ copy_v3_v3(use_background_color, &scene->world->horr);
+ use_background_color[3] = scene->r.alphamode ? 0.0f : 1.0f;
+
+ DRW_shgroup_uniform_vec4(
+ stl->g_data->dpix_preview_shgrp, "background_color", use_background_color, 1);
+
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "depth_offset", &stl->g_data->dpix_depth_offset, 1);
+ DRW_shgroup_uniform_float(stl->g_data->dpix_preview_shgrp,
+ "depth_width_influence",
+ &lanpr->depth_width_influence,
+ 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "depth_width_curve", &lanpr->depth_width_curve, 1);
+ DRW_shgroup_uniform_float(stl->g_data->dpix_preview_shgrp,
+ "depth_alpha_influence",
+ &lanpr->depth_alpha_influence,
+ 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "depth_alpha_curve", &lanpr->depth_alpha_curve, 1);
+ static float unit_thickness = 1.0f;
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "line_thickness", &ll->thickness, 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp,
+ "thickness_contour",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? &unit_thickness : &ll->contour.thickness,
+ 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp,
+ "thickness_crease",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? &unit_thickness : &ll->crease.thickness,
+ 1);
+ DRW_shgroup_uniform_float(stl->g_data->dpix_preview_shgrp,
+ "thickness_material",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &unit_thickness :
+ &ll->material_separate.thickness,
+ 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp,
+ "thickness_edge_mark",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ? &unit_thickness : &ll->edge_mark.thickness,
+ 1);
+ DRW_shgroup_uniform_float(stl->g_data->dpix_preview_shgrp,
+ "thickness_intersection",
+ (ll->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) ?
+ &unit_thickness :
+ &ll->intersection.thickness,
+ 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "z_near", &stl->g_data->dpix_znear, 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "z_far", &stl->g_data->dpix_zfar, 1);
+
+ ED_lanpr_calculate_normal_object_vector(ll, normal_object_direction);
+
+ static int normal_effect_inverse;
+ static int zero_value = 0;
+ normal_effect_inverse = (ll->flags & LANPR_LINE_LAYER_NORMAL_INVERSE);
+
+ DRW_shgroup_uniform_int(stl->g_data->dpix_preview_shgrp,
+ "normal_mode",
+ (ll->flags & LANPR_LINE_LAYER_NORMAL_ENABLED) ? &ll->normal_mode :
+ &zero_value,
+ 1);
+ DRW_shgroup_uniform_int(
+ stl->g_data->dpix_preview_shgrp, "normal_effect_inverse", &normal_effect_inverse, 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "normal_ramp_begin", &ll->normal_ramp_begin, 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "normal_ramp_end", &ll->normal_ramp_end, 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "normal_thickness_start", &ll->normal_thickness_start, 1);
+ DRW_shgroup_uniform_float(
+ stl->g_data->dpix_preview_shgrp, "normal_thickness_end", &ll->normal_thickness_end, 1);
+ DRW_shgroup_uniform_vec3(
+ stl->g_data->dpix_preview_shgrp, "normal_direction", normal_object_direction, 1);
+
+ pd->begin_index = 0;
+ int fsize = sizeof(float) * 4 * texture_size * texture_size;
+
+ if (lanpr_share.dpix_reloaded) {
+ pd->atlas_pl = MEM_callocN(fsize, "atlas_point_l");
+ pd->atlas_pr = MEM_callocN(fsize, "atlas_point_r");
+ pd->atlas_nl = MEM_callocN(fsize, "atlas_normal_l");
+ pd->atlas_nr = MEM_callocN(fsize, "atlas_normal_l");
+ pd->atlas_edge_mask = MEM_callocN(fsize, "atlas_edge_mask"); /* should always be float */
+
+ lanpr_dpix_batch_free();
+
+ BLI_mempool_clear(lanpr_share.mp_batch_list);
+ }
+ }
+ else if (lanpr->master_mode == LANPR_MASTER_MODE_SOFTWARE) {
+ ;
+ }
+
+ /* Intersection cache must be calculated before drawing. */
+ if (draw_ctx->scene->lanpr.flags & LANPR_AUTO_UPDATE) {
+
+ /** This will be released by compute function when loading is finished. */
+ BLI_spin_lock(&lanpr_share.lock_loader);
+
+ if (draw_ctx->scene->lanpr.master_mode == LANPR_MASTER_MODE_SOFTWARE) {
+ if (is_render) {
+ ED_lanpr_compute_feature_lines_internal(draw_ctx->depsgraph, 0);
+ }
+ else {
+ ED_lanpr_compute_feature_lines_background(draw_ctx->depsgraph, 0);
+ }
+ }
+ else if (draw_ctx->scene->lanpr.master_mode == LANPR_MASTER_MODE_DPIX) {
+ /* Don't do threaded intersection calculation. It's pointless in GPU mode anyway. */
+ ED_lanpr_compute_feature_lines_internal(draw_ctx->depsgraph, 1);
+ }
+ }
+
+ /** Important: This ensures we don't proceed beyond this point until everything is loaded. */
+ BLI_spin_lock(&lanpr_share.lock_loader);
+ BLI_spin_unlock(&lanpr_share.lock_loader);
+
+ if (ED_lanpr_calculation_flag_check(LANPR_RENDER_FINISHED) ||
+ ED_lanpr_calculation_flag_check(LANPR_RENDER_IDLE)) {
+ ED_lanpr_rebuild_all_command(draw_ctx->scene);
+ ED_lanpr_calculation_set_flag(LANPR_RENDER_IDLE);
+ }
+ else if (!is_render) {
+ DRW_viewport_request_redraw();
+ }
+}
+
+static void lanpr_cache_populate(void *vedata, Object *ob)
+{
+
+ LANPR_StorageList *stl = ((LANPR_Data *)vedata)->stl;
+ LANPR_PrivateData *pd = stl->g_data;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ SceneLANPR *lanpr = &draw_ctx->scene->lanpr;
+ int usage = OBJECT_FEATURE_LINE_INHERENT, dpix_ok = 0;
+
+ if (!DRW_object_is_renderable(ob)) {
+ return;
+ }
+ if (ob == draw_ctx->object_edit) {
+ return;
+ }
+ if (ob->type != OB_MESH) {
+ return;
+ }
+
+ struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
+ if (geom) {
+ if ((dpix_ok = (lanpr->master_mode == LANPR_MASTER_MODE_DPIX && lanpr->active_layer &&
+ !lanpr_share.dpix_shader_error)) != 0) {
+ usage = ED_lanpr_object_collection_usage_check(draw_ctx->scene->master_collection, ob);
+ if (usage == OBJECT_FEATURE_LINE_EXCLUDE) {
+ return;
+ }
+ }
+ DRW_shgroup_call_no_cull(stl->g_data->multipass_shgrp, geom, ob);
+ }
+
+ if (dpix_ok) {
+
+ /* usage already set */
+ if (usage == OBJECT_FEATURE_LINE_OCCLUSION_ONLY) {
+ return;
+ }
+
+ int idx = pd->begin_index;
+ if (lanpr_share.dpix_reloaded) {
+ pd->begin_index = lanpr_feed_atlas_data_obj(vedata,
+ pd->atlas_pl,
+ pd->atlas_pr,
+ pd->atlas_nl,
+ pd->atlas_nr,
+ pd->atlas_edge_mask,
+ ob,
+ idx);
+ if (idx != pd->begin_index) { /* Which means we are actually able to feed object data */
+ lanpr_feed_atlas_trigger_preview_obj(vedata, ob, idx);
+ }
+ }
+ }
+}
+
+static void lanpr_cache_finish(void *vedata)
+{
+ LANPR_StorageList *stl = ((LANPR_Data *)vedata)->stl;
+ LANPR_PrivateData *pd = stl->g_data;
+ LANPR_TextureList *txl = ((LANPR_Data *)vedata)->txl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ SceneLANPR *lanpr = &draw_ctx->scene->lanpr;
+ float mat[4][4];
+ unit_m4(mat);
+
+ if (lanpr->master_mode == LANPR_MASTER_MODE_DPIX && lanpr->active_layer &&
+ !lanpr_share.dpix_shader_error) {
+ if (lanpr_share.dpix_reloaded) {
+ if (lanpr_share.render_buffer_shared) {
+ lanpr_feed_atlas_data_intersection_cache(vedata,
+ pd->atlas_pl,
+ pd->atlas_pr,
+ pd->atlas_nl,
+ pd->atlas_nr,
+ pd->atlas_edge_mask,
+ pd->begin_index);
+ lanpr_create_atlas_intersection_preview(vedata, pd->begin_index);
+ }
+ GPU_texture_update(txl->dpix_in_pl, GPU_DATA_FLOAT, pd->atlas_pl);
+ GPU_texture_update(txl->dpix_in_pr, GPU_DATA_FLOAT, pd->atlas_pr);
+ GPU_texture_update(txl->dpix_in_nl, GPU_DATA_FLOAT, pd->atlas_nl);
+ GPU_texture_update(txl->dpix_in_nr, GPU_DATA_FLOAT, pd->atlas_nr);
+ GPU_texture_update(txl->dpix_in_edge_mask, GPU_DATA_FLOAT, pd->atlas_edge_mask);
+
+ MEM_freeN(pd->atlas_pl);
+ MEM_freeN(pd->atlas_pr);
+ MEM_freeN(pd->atlas_nl);
+ MEM_freeN(pd->atlas_nr);
+ MEM_freeN(pd->atlas_edge_mask);
+ pd->atlas_pl = 0;
+ lanpr_share.dpix_reloaded = 0;
+ }
+
+ LANPR_BatchItem *bi;
+ for (bi = lanpr_share.dpix_batch_list.first; bi; bi = (void *)bi->item.next) {
+ DRW_shgroup_call_ex(
+ pd->dpix_transform_shgrp, 0, bi->ob->obmat, bi->dpix_transform_batch, true, NULL);
+ DRW_shgroup_call(pd->dpix_preview_shgrp, bi->dpix_preview_batch, 0);
+ }
+
+ if (lanpr_share.render_buffer_shared &&
+ lanpr_share.render_buffer_shared->DPIXIntersectionBatch) {
+ DRW_shgroup_call(pd->dpix_transform_shgrp,
+ lanpr_share.render_buffer_shared->DPIXIntersectionTransformBatch,
+ 0);
+ DRW_shgroup_call(
+ pd->dpix_preview_shgrp, lanpr_share.render_buffer_shared->DPIXIntersectionBatch, 0);
+ }
+ }
+}
+
+static void lanpr_draw_scene_exec(void *vedata, GPUFrameBuffer *dfb, int is_render)
+{
+ LANPR_PassList *psl = ((LANPR_Data *)vedata)->psl;
+ LANPR_TextureList *txl = ((LANPR_Data *)vedata)->txl;
+ LANPR_StorageList *stl = ((LANPR_Data *)vedata)->stl;
+ LANPR_FramebufferList *fbl = ((LANPR_Data *)vedata)->fbl;
+
+ float clear_col[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+ float clear_depth = 1.0f;
+ uint clear_stencil = 0xFF;
+
+ GPU_framebuffer_bind(fbl->passes);
+ eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT | GPU_COLOR_BIT;
+ GPU_framebuffer_clear(fbl->passes, clear_bits, clear_col, clear_depth, clear_stencil);
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+ SceneLANPR *lanpr = &scene->lanpr;
+
+ if (lanpr->master_mode == LANPR_MASTER_MODE_DPIX && !lanpr_share.dpix_shader_error) {
+ DRW_draw_pass(psl->color_pass);
+ lanpr_dpix_draw_scene(txl, fbl, psl, stl->g_data, lanpr, dfb, is_render);
+ }
+ else if (lanpr->master_mode == LANPR_MASTER_MODE_SOFTWARE) {
+ lanpr_software_draw_scene(vedata, dfb, is_render);
+ }
+}
+
+static void lanpr_draw_scene(void *vedata)
+{
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+
+ lanpr_draw_scene_exec(vedata, dfbl->default_fb, 0);
+}
+
+static void lanpr_render_cache(void *vedata,
+ struct Object *ob,
+ struct RenderEngine *UNUSED(engine),
+ struct Depsgraph *UNUSED(depsgraph))
+{
+
+ lanpr_cache_populate(vedata, ob);
+}
+
+static void lanpr_render_matrices_init(RenderEngine *engine, Depsgraph *depsgraph)
+{
+ /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
+ float frame = BKE_scene_frame_get(scene);
+
+ /* Set the persective, view and window matrix. */
+ float winmat[4][4], wininv[4][4];
+ float viewmat[4][4], viewinv[4][4];
+ float persmat[4][4], persinv[4][4];
+ float unitmat[4][4];
+
+ RE_GetCameraWindow(engine->re, ob_camera_eval, winmat);
+ RE_GetCameraModelMatrix(engine->re, ob_camera_eval, viewinv);
+
+ invert_m4_m4(viewmat, viewinv);
+ mul_m4_m4m4(persmat, winmat, viewmat);
+ invert_m4_m4(persinv, persmat);
+ invert_m4_m4(wininv, winmat);
+
+ unit_m4(unitmat);
+
+ DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
+ DRW_view_default_set(view);
+ DRW_view_set_active(view);
+}
+
+static int LANPR_GLOBAL_update_tag;
+
+static void lanpr_id_update(void *UNUSED(vedata), ID *id)
+{
+ /* if (vedata->engine_type != &draw_engine_lanpr_type) return; */
+
+ /* Handle updates based on ID type. */
+ switch (GS(id->name)) {
+ case ID_WO:
+ case ID_OB:
+ case ID_ME:
+ LANPR_GLOBAL_update_tag = 1;
+ default:
+ /* pass */
+ break;
+ }
+}
+
+static void lanpr_render_to_image(void *vedata,
+ RenderEngine *engine,
+ struct RenderLayer *render_layer,
+ const rcti *rect)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ lanpr_share.re_render = engine;
+
+ RE_engine_update_stats(engine, NULL, "LANPR: Initializing");
+
+ lanpr_render_matrices_init(engine, draw_ctx->depsgraph);
+
+ /* refered to eevee's code */
+
+ /* Init default FB and render targets:
+ * In render mode the default framebuffer is not generated
+ * because there is no viewport. So we need to manually create it or
+ * not use it. For code clarity we just allocate it make use of it. */
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+
+ DRW_texture_ensure_fullscreen_2d(&dtxl->depth, GPU_DEPTH_COMPONENT32F, 0);
+ DRW_texture_ensure_fullscreen_2d(&dtxl->color, GPU_RGBA32F, 0);
+
+ GPU_framebuffer_ensure_config(
+ &dfbl->default_fb,
+ {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)});
+
+ lanpr_engine_init(vedata);
+
+ /* force dpix batch to re-create */
+ lanpr_share.dpix_reloaded_deg = 1;
+
+ /* force use actual camera instead of viewport camera. */
+ /* Do this before creating render buffer. */
+ lanpr_share.viewport_camera_override = -1;
+ lanpr_cache_init(vedata);
+ DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, lanpr_render_cache);
+ lanpr_cache_finish(vedata);
+
+ /* get ref for destroy data */
+ /* lanpr_share.rb_ref = lanpr->render_buffer; */
+
+ DRW_render_instance_buffer_finish();
+
+ float clear_col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ float clear_depth = 1.0f;
+ uint clear_stencil = 0xFF;
+ eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT | GPU_COLOR_BIT;
+
+ GPU_framebuffer_bind(dfbl->default_fb);
+ GPU_framebuffer_clear(dfbl->default_fb, clear_bits, clear_col, clear_depth, clear_stencil);
+
+ lanpr_draw_scene_exec(vedata, dfbl->default_fb, 1);
+
+ /* read it back so we can again display and save it. */
+ const char *viewname = RE_GetActiveRenderView(engine->re);
+ RenderPass *rp = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
+ GPU_framebuffer_bind(dfbl->default_fb);
+ GPU_framebuffer_read_color(dfbl->default_fb,
+ rect->xmin,
+ rect->ymin,
+ BLI_rcti_size_x(rect),
+ BLI_rcti_size_y(rect),
+ 4,
+ 0,
+ rp->rect);
+
+ /* Must clear to avoid other problems. */
+ lanpr_share.re_render = NULL;
+}
+
+static void lanpr_view_update(void *UNUSED(vedata))
+{
+ lanpr_share.dpix_reloaded_deg = 1; /* very bad solution, this will slow down animation. */
+}
+
+static const DrawEngineDataSize lanpr_data_size = DRW_VIEWPORT_DATA_SIZE(LANPR_Data);
+
+DrawEngineType draw_engine_lanpr_type = {
+ NULL,
+ NULL,
+ N_("LANPR"),
+ &lanpr_data_size,
+ &lanpr_engine_init,
+ &lanpr_engine_free,
+ &lanpr_cache_init,
+ &lanpr_cache_populate,
+ &lanpr_cache_finish,
+ NULL, /* draw background */
+ &lanpr_draw_scene, /* draw scene */
+ &lanpr_view_update,
+ &lanpr_id_update,
+ &lanpr_render_to_image,
+};
+
+RenderEngineType DRW_engine_viewport_lanpr_type = {NULL,
+ NULL,
+ LANPR_ENGINE,
+ N_("LANPR"),
+ RE_INTERNAL,
+ NULL, /* update */
+ &DRW_render_to_image, /* render to img */
+ NULL, /* bake */
+ NULL, /* view update */
+ NULL, /* render to view */
+ NULL, /* update in script */
+ NULL, /* update in render pass */
+ &draw_engine_lanpr_type,
+ {NULL, NULL, NULL}};
diff --git a/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_frag.glsl b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_frag.glsl
new file mode 100644
index 00000000000..5801f591916
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_frag.glsl
@@ -0,0 +1,6 @@
+in vec4 color_out;
+
+void main()
+{
+ gl_FragData[0] = vec4(color_out.rgb, 1);
+}
diff --git a/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_geom.glsl b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_geom.glsl
new file mode 100644
index 00000000000..a9bb9d4c62d
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_preview_geom.glsl
@@ -0,0 +1,193 @@
+layout(points) in;
+layout(triangle_strip, max_vertices = 6) out;
+
+uniform sampler2D tex_vert0; // L
+uniform sampler2D tex_vert1; // R
+uniform sampler2D tex_fnormal0;
+uniform sampler2D tex_fnormal1; // caution: these are face normals!
+uniform sampler2D tex_edge_mask;
+
+// uniform float uValue0; // buffer_w
+uniform vec4 viewport; // viewport
+uniform float depth_offset;
+
+// these are for depth related thickness control;
+uniform float line_thickness;
+uniform float depth_width_influence;
+uniform float depth_width_curve;
+uniform float depth_alpha_influence;
+uniform float depth_alpha_curve;
+uniform float z_near;
+uniform float z_far;
+
+uniform vec4 background_color;
+
+uniform vec4 color_contour;
+uniform vec4 color_crease;
+uniform vec4 color_material;
+uniform vec4 color_edge_mark;
+uniform vec4 color_intersection;
+
+uniform float thickness_contour;
+uniform float thickness_crease;
+uniform float thickness_material;
+uniform float thickness_edge_mark;
+uniform float thickness_intersection;
+
+// the same as software mode
+uniform int normal_mode;
+uniform int normal_effect_inverse;
+uniform vec3 normal_direction; // also used as point position
+uniform float normal_ramp_begin;
+uniform float normal_ramp_end;
+uniform float normal_thickness_start;
+uniform float normal_thickness_end;
+
+float use_thickness;
+
+out vec4 color_out;
+
+vec4 use_color;
+
+float get_linear_depth(float z)
+{
+ float ze = 2.0 * z_near * z_far / (z_far + z_near - z * (z_far - z_near));
+ return (ze - z_near) / (z_far - z_near);
+}
+
+float curve_01(float z, float factor)
+{
+ return pow(z, 1 - factor); // factor is -inf~1-eps
+}
+
+vec4 apply_scale(vec4 center, vec4 a)
+{
+ float lz = get_linear_depth(center.z);
+ float depth_factor = mix(0, curve_01(lz, depth_width_curve), depth_width_influence);
+
+ return mix(a, center, depth_factor);
+}
+
+void emit_color_and_alpha(vec4 a, int is_crease, float crease_fading)
+{
+ float lz = get_linear_depth(a.z);
+ float alpha_factor = mix(0, curve_01(lz, depth_alpha_curve), depth_alpha_influence);
+ float alpha_crease_fading = alpha_factor;
+ if (is_crease > 0)
+ alpha_crease_fading = mix(alpha_factor, 1, crease_fading * 2); // fading=0.5 -> fade all
+ color_out = vec4(use_color.rgb, mix(1, 0, alpha_crease_fading));
+}
+
+void draw_line(vec4 p1, vec4 p2, int is_crease)
+{
+
+ vec4 Line = p2 - p1;
+ vec4 Normal = normalize(vec4(-Line.y, Line.x, 0, 0));
+
+ vec4 a, b, c, d;
+
+ vec4 offset = Normal * use_thickness * 0.001;
+
+ // correct thickness
+ offset.x *= viewport.w / viewport.z;
+
+ a = p1 + offset;
+ b = p1 - offset;
+ c = p2 + offset;
+ d = p2 - offset;
+
+ a = apply_scale(p1, a);
+ b = apply_scale(p1, b);
+ c = apply_scale(p2, c);
+ d = apply_scale(p2, d);
+
+ gl_Position = vec4(a.xy, a.z - depth_offset, 1);
+ emit_color_and_alpha(a, is_crease, p2.w);
+ EmitVertex();
+ gl_Position = vec4(b.xy, b.z - depth_offset, 1);
+ emit_color_and_alpha(b, is_crease, p2.w);
+ EmitVertex();
+ gl_Position = vec4(c.xy, c.z - depth_offset, 1);
+ emit_color_and_alpha(c, is_crease, p2.w);
+ EmitVertex();
+
+ gl_Position = vec4(b.xy, b.z - depth_offset, 1);
+ emit_color_and_alpha(b, is_crease, p2.w);
+ EmitVertex();
+ gl_Position = vec4(c.xy, c.z - depth_offset, 1);
+ emit_color_and_alpha(c, is_crease, p2.w);
+ EmitVertex();
+ gl_Position = vec4(d.xy, d.z - depth_offset, 1);
+ emit_color_and_alpha(d, is_crease, p2.w);
+ EmitVertex();
+
+ EndPrimitive();
+}
+
+float factor_to_thickness(float factor)
+{
+ float r = (factor - normal_ramp_begin) / (normal_ramp_end - normal_ramp_begin);
+ if (r > 1)
+ r = 1;
+ if (r < 0)
+ r = 0;
+ float thickness = normal_effect_inverse == 1 ?
+ mix(normal_thickness_start, normal_thickness_end, r) :
+ mix(normal_thickness_end, normal_thickness_start, r);
+ return thickness;
+}
+
+void main()
+{
+ vec4 p1 = texelFetch(tex_vert0, ivec2(gl_in[0].gl_Position.xy), 0);
+ vec4 p2 = texelFetch(tex_vert1, ivec2(gl_in[0].gl_Position.xy), 0);
+
+ vec4 n1 = texelFetch(tex_fnormal0, ivec2(gl_in[0].gl_Position.xy), 0);
+ vec4 n2 = texelFetch(tex_fnormal1, ivec2(gl_in[0].gl_Position.xy), 0);
+
+ vec3 use_normal = normalize(mix(n1, n2, 0.5).xyz);
+
+ if (p1.w == 0 && p2.w == 0)
+ return;
+
+ vec4 edge_mask = texelFetch(tex_edge_mask, ivec2(gl_in[0].gl_Position.xy), 0);
+
+ int is_crease = 0;
+
+ float th = line_thickness;
+ if (normal_mode == 0) {
+ th = line_thickness;
+ }
+ else if (normal_mode == 1) {
+ float factor = dot(use_normal, normal_direction);
+ th = factor_to_thickness(factor);
+ }
+ else if (normal_mode == 2) {
+ float factor = dot(use_normal, normal_direction);
+ th = factor_to_thickness(factor);
+ }
+
+ if (edge_mask.g > 0) {
+ use_color = color_edge_mark;
+ use_thickness = th * thickness_edge_mark;
+ }
+ else if (edge_mask.r > 0) {
+ use_color = color_material;
+ use_thickness = th * thickness_material;
+ }
+ else if (edge_mask.b > 0) {
+ use_color = color_intersection;
+ use_thickness = th * thickness_intersection;
+ }
+ else if (p2.w != p1.w) {
+ use_color = color_crease;
+ use_thickness = th * thickness_crease;
+ is_crease = 1;
+ }
+ else {
+ use_color = color_contour;
+ use_thickness = th * thickness_contour;
+ }
+
+ draw_line(p1, p2, is_crease);
+}
diff --git a/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_clip_frag.glsl b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_clip_frag.glsl
new file mode 100644
index 00000000000..01a15b4e52e
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_clip_frag.glsl
@@ -0,0 +1,469 @@
+uniform mat4 ModelMatrix;
+uniform mat4 ViewMatrix;
+uniform mat4 ViewMatrixInverse;
+uniform mat4 ProjectionMatrix;
+uniform mat4 ProjectionMatrixInverse;
+
+uniform int use_contour;
+uniform int use_crease;
+uniform int use_material;
+uniform int use_edge_mark;
+uniform int use_intersection;
+
+uniform float crease_threshold;
+uniform float crease_fade_threshold;
+
+uniform int is_perspective; // persp and orth use different crease line determin method
+
+// uniform float sample_step; // length calculation unused now.
+// uniform int buffer_width;
+
+uniform vec4 viewport;
+
+uniform sampler2D tex_vert0;
+uniform sampler2D tex_vert1;
+uniform sampler2D tex_fnormal0;
+uniform sampler2D tex_fnormal1;
+uniform sampler2D tex_edge_mask;
+
+// uniform sampler2D TexSample4;
+//#define path_start_end_ptrs TexSample4 // edge adjacent data
+
+// calculate in shader
+
+vec3 view_pos;
+vec3 view_dir;
+
+int is_crease; // we calculate crease in GPU because it's faster and we have normal data anyway.
+ // and we need to indicate crease test success result using p1.w==1 && p2.w==0
+
+float crease_strength;
+
+// these are for adapting argument names...
+#define modelview (ViewMatrix * ModelMatrix)
+#define projection ProjectionMatrix
+#define inverse_projection ProjectionMatrixInverse
+
+// ivec2 getTexturePix(vec2 fb_coord){
+// vec2 n = ((fb_coord+vec2(1,1))/2).xy;
+// return ivec2(n.x*buffer_width,n.y*buffer_width);
+//}
+
+// Amount of padding around a segment in the segment atlas.
+// The amount of padding rolls off to zero for short segments,
+// and is zero for segments in the middle of paths.
+
+vec2 segmentPadding(float num_samples, float index, float start_index, float end_index)
+{
+ const float MAX_PADDING = 10.0;
+
+ float amount = floor(clamp((num_samples - 2.0) * 0.5, 0.0, MAX_PADDING));
+
+ float left = amount * max(1.0 + start_index - index, 0.0);
+ float right = amount * max(1.0 + index - end_index, 0.0);
+
+ return vec2(left, right);
+}
+
+// Converting from linear indices to 2D coordinates and back:
+
+float coordinateToIndex(vec2 coord, float buf_size)
+{
+ vec2 floor_coord = floor(coord);
+ return floor_coord.x + floor_coord.y * buf_size;
+}
+
+vec2 indexToCoordinate(float index, float buf_size)
+{
+ return vec2(mod(index, buf_size), floor(index / buf_size));
+}
+
+// Packing and unpacking values in the segment atlas offset texture:
+
+float unpackNumSamples(vec4 offset_texel)
+{
+ return offset_texel.b;
+}
+
+float unpackArcLength(vec4 offset_texel)
+{
+ return offset_texel.a;
+}
+
+float unpackSampleOffset(vec4 offset_texel)
+{
+ return offset_texel.r;
+}
+
+float unpackArcLengthOffset(vec4 offset_texel)
+{
+ return offset_texel.g;
+}
+
+vec4 packOffsetTexel(float num_samples, float arc_length)
+{
+ return vec4(num_samples, arc_length, num_samples, arc_length);
+}
+
+vec4 packOffsetTexel(float num_samples,
+ float arc_length,
+ float num_samples_offset,
+ float arc_length_offset)
+{
+ return vec4(num_samples_offset, arc_length_offset, num_samples, arc_length);
+}
+
+// Packing and unpacking values in the 3D vertex positions:
+
+float unpackPathStart(vec4 texel)
+{
+ return texel.r;
+}
+
+float unpackPathEnd(vec4 texel)
+{
+ return texel.g;
+}
+
+float unpackPathLength(vec4 texel)
+{
+ return texel.b;
+}
+
+// Projecting and unprojecting:
+
+vec2 clipToWindow(sampler2D clip_positions, vec4 viewport, ivec2 coordinate)
+{
+ vec4 clip = texelFetch(clip_positions, coordinate, 0);
+ vec3 post_div = clip.xyz / clip.w;
+ return (post_div.xy + vec2(1.0, 1.0)) * 0.5 * viewport.zw;
+}
+
+vec2 clipToWindow(vec4 clip, vec4 viewport)
+{
+ vec3 post_div = clip.xyz / clip.w;
+ return (post_div.xy + vec2(1.0, 1.0)) * 0.5 * viewport.zw;
+}
+
+// Path id encoding and decoding.
+
+bool idEqualGreaterThan(vec3 a, vec3 b)
+{
+ float ida = a.b * 256.0 * 256.0 + a.g * 256.0 + a.r;
+ float idb = b.b * 256.0 * 256.0 + b.g * 256.0 + b.r;
+ const float small = 0.001;
+ return ida - idb > -small;
+}
+
+bool idsEqual(vec3 a, vec3 b)
+{
+ float ida = a.b * 256.0 * 256.0 + a.g * 256.0 + a.r;
+ float idb = b.b * 256.0 * 256.0 + b.g * 256.0 + b.r;
+ const float small = 0.001;
+ return abs(ida - idb) < small;
+}
+
+vec3 idToColor(float id)
+{
+ id = id + 1.0;
+ float blue = floor(id / (256.0 * 256.0));
+ float green = floor(id / 256.0) - blue * 256.0;
+ float red = id - green * 256.0 - blue * 256.0 * 256.0;
+ return vec3(red, green, blue) / 255.0;
+}
+
+struct segment {
+ vec3 p1;
+ vec3 p2;
+ bool on_screen;
+};
+
+float epsilon = 0.00001;
+float xmin = -1.1;
+float xmax = 1.1;
+float ymin = -1.1;
+float ymax = 1.1;
+
+// this is a conservative offscreen rejection test ... catches most cases
+bool segmentOffScreen(vec3 p0, vec3 p1)
+{
+ return ((p0[0] < xmin && p1[0] < xmin) || (p0[0] > xmax && p1[0] > xmax) ||
+ (p0[1] < ymin && p1[1] < ymin) || (p0[1] > ymax && p1[1] > ymax));
+}
+
+bool pointOffScreen(vec3 p)
+{
+ return (p[0] < xmin || p[0] > xmax || p[1] < ymin || p[1] > ymax);
+}
+
+vec3 clipMinMaxX(vec3 outv, vec3 inv)
+{
+ vec3 ret = outv;
+ if (outv.x < xmin) {
+ float t = (xmin - outv.x) / (inv.x - outv.x);
+ ret = t * inv + (1.0 - t) * outv;
+ }
+ else if (outv.x > xmax) {
+ float t = (xmax - inv.x) / (outv.x - inv.x);
+ ret = t * outv + (1.0 - t) * inv;
+ }
+ return ret;
+}
+
+vec3 clipMinMaxY(vec3 outv, vec3 inv)
+{
+ vec3 ret = outv;
+ if (outv.y < ymin) {
+ float t = (ymin - outv.y) / (inv.y - outv.y);
+ ret = t * inv + (1.0 - t) * outv;
+ }
+ else if (outv.y > ymax) {
+ float t = (ymax - inv.y) / (outv.y - inv.y);
+ ret = t * outv + (1.0 - t) * inv;
+ }
+ return ret;
+}
+
+vec3 clipSegmentOneOut(vec3 off_screen, vec3 on_screen)
+{
+ vec3 outv = off_screen;
+
+ // first clip against the x coords
+ outv = clipMinMaxX(outv, on_screen);
+
+ // now clip against the y coords using the newly clipped point
+ outv = clipMinMaxY(outv, on_screen);
+
+ return outv;
+}
+
+segment clipToMin(float min, segment inseg, float p1val, float p2val)
+{
+ float minPos = min + epsilon;
+ float minNeg = min - epsilon;
+ segment outseg = segment(inseg.p1, inseg.p2, inseg.on_screen);
+
+ // trivial reject
+ if ((p1val < minPos && p2val < minPos) || inseg.on_screen == false) {
+ outseg.on_screen = false;
+ }
+
+ // cut at min
+ if (p1val < minPos) {
+ float t = (min - p1val) / (p2val - p1val);
+ outseg.p1 = t * inseg.p2 + (1.0 - t) * inseg.p1;
+ }
+ else if (p2val < minPos) {
+ float t = (min - p2val) / (p1val - p2val);
+ outseg.p2 = t * inseg.p1 + (1.0 - t) * inseg.p2;
+ }
+ return outseg;
+}
+
+segment clipToMax(float max, segment inseg, float p1val, float p2val)
+{
+ float maxPos = max + epsilon;
+ float maxNeg = max - epsilon;
+ segment outseg = segment(inseg.p1, inseg.p2, inseg.on_screen);
+
+ // trivial reject
+ if ((p1val > maxNeg && p2val > maxNeg) || inseg.on_screen == false) {
+ outseg.on_screen = false;
+ }
+
+ // cut at max
+ if (p1val > maxNeg) {
+ float t = (max - p2val) / (p1val - p2val);
+ outseg.p1 = t * inseg.p1 + (1.0 - t) * inseg.p2;
+ }
+ else if (p2val > maxNeg) {
+ float t = (max - p1val) / (p2val - p1val);
+ outseg.p2 = t * inseg.p2 + (1.0 - t) * inseg.p1;
+ }
+ return outseg;
+}
+
+segment clipSegmentBothOut(vec3 p1, vec3 p2)
+{
+ segment seg = segment(p1, p2, true);
+
+ seg = clipToMin(xmin, seg, seg.p1.x, seg.p2.x);
+ seg = clipToMax(xmax, seg, seg.p1.x, seg.p2.x);
+ seg = clipToMin(ymin, seg, seg.p1.y, seg.p2.y);
+ seg = clipToMax(ymax, seg, seg.p1.y, seg.p2.y);
+
+ return seg;
+}
+
+vec3 clipSegmentToNear(vec3 off_screen, vec3 on_screen)
+{
+ // see http://members.tripod.com/~Paul_Kirby/vector/Vplanelineint.html
+
+ vec3 a = off_screen;
+ vec3 b = on_screen;
+ vec3 c = view_pos + view_dir;
+ vec3 n = view_dir;
+ float t = dot((c - a), n) / dot((b - a), n);
+
+ vec3 clipped = a + (b - a) * t;
+ return clipped;
+}
+
+bool pointBeyondNear(vec3 p)
+{
+ vec3 offset = p - view_pos;
+ bool beyond = dot(offset, view_dir) > 0.0;
+ return beyond;
+}
+
+// 1 for contour 2 for others
+int testProfileEdge(ivec2 texcoord, vec3 world_position)
+{
+ // This should really be the inverse transpose of the modelview matrix, but
+ // that only matters if the camera has a weird anisotropic scale or skew.
+
+ mat3 nm = mat3(transpose(inverse(ModelMatrix)));
+ vec3 face_normal_0 = mat3(nm) * texelFetch(tex_fnormal0, texcoord, 0).xyz;
+ vec3 face_normal_1 = mat3(nm) * texelFetch(tex_fnormal1, texcoord, 0).xyz;
+ vec3 camera_to_line = is_perspective == 1 ? world_position - view_pos :
+ view_dir; // modelview * vec4(world_position, 1.0);
+
+ vec4 edge_mask = texelFetch(tex_edge_mask, texcoord, 0);
+
+ float dot0 = dot(camera_to_line.xyz, vec3(face_normal_0.xyz));
+ float dot1 = dot(camera_to_line.xyz, vec3(face_normal_1.xyz));
+ float dot2 = dot(normalize(vec3(face_normal_0.xyz)), normalize(vec3(face_normal_1.xyz)));
+
+ bool contour = (dot0 >= 0.0 && dot1 <= 0.0) || (dot0 <= 0.0 && dot1 >= 0.0);
+
+ is_crease = (((use_contour > 0 && !contour) || use_contour == 0) &&
+ ((dot2 < crease_threshold) || (dot2 < crease_fade_threshold))) ?
+ 1 :
+ 0;
+
+ crease_strength = (is_crease > 0 && dot2 > crease_threshold) ?
+ ((dot2 - crease_threshold) / (crease_fade_threshold - crease_threshold) /
+ 2) :
+ 0;
+ // use 0 to 0.5 to repesent the range, because 1 will represent another meaning
+
+ if (use_contour > 0 && contour)
+ return 1;
+ if (((use_crease > 0) && (is_crease > 0)) || ((use_material > 0) && (edge_mask.r > 0)) ||
+ ((use_edge_mark > 0) && (edge_mask.g > 0)) ||
+ ((use_intersection > 0) && (edge_mask.b > 0)) || false)
+ return 2;
+ return 0;
+}
+
+void main()
+{
+
+ view_dir = -mat3(ViewMatrixInverse) * vec3(0, 0, 1);
+ view_pos = (ViewMatrixInverse)[3].xyz;
+
+ xmin *= viewport.z / viewport.w;
+ xmax *= viewport.z / viewport.w;
+
+ // look up the world positions of the segment vertices
+ ivec2 texcoord = ivec2(gl_FragCoord.xy);
+
+ vec4 v0_world_pos = texelFetch(tex_vert0, texcoord, 0);
+ vec4 v1_world_pos = texelFetch(tex_vert1, texcoord, 0);
+ v0_world_pos = ModelMatrix * vec4(v0_world_pos.xyz, 1);
+ v1_world_pos = ModelMatrix * vec4(v1_world_pos.xyz, 1);
+
+ // early exit if there are no vertices here to process
+ if (v0_world_pos.w < 0.5) {
+ // no vertex data to process
+ gl_FragData[0] = vec4(0.5, 0.0, 0.0, 0.0);
+ gl_FragData[1] = vec4(0.5, 0.5, 0.0, 0.0);
+ // must write something into fragdata[2] to prevent
+ // buffer 2 from getting filled with garbage? (very weird)
+ gl_FragData[2] = vec4(0.0, 1.0, 0.0, 0.0);
+ return;
+ }
+
+ vec3 v0_clipped_near = v0_world_pos.xyz;
+ vec3 v1_clipped_near = v1_world_pos.xyz;
+
+ if (is_perspective == 1) {
+ // clip to the near plane
+ bool v0_beyond_near = pointBeyondNear(v0_world_pos.xyz);
+ bool v1_beyond_near = pointBeyondNear(v1_world_pos.xyz);
+
+ if (!v0_beyond_near && !v1_beyond_near) {
+ // segment entirely behind the camera
+ gl_FragData[0] = vec4(0.0, 1.0, 0.0, 0.0);
+ gl_FragData[1] = vec4(0.0, 0.0, 1.0, 0.0);
+ gl_FragData[2] = vec4(0.0, 1.0, 0.0, 0.0);
+ return;
+ }
+ else if (!v0_beyond_near) {
+ v0_clipped_near = clipSegmentToNear(v0_world_pos.xyz, v1_clipped_near);
+ }
+ else if (!v1_beyond_near) {
+ v1_clipped_near = clipSegmentToNear(v1_world_pos.xyz, v0_clipped_near);
+ }
+ }
+
+ int profile_on = testProfileEdge(texcoord, v0_clipped_near);
+ if (profile_on == 0) {
+ // Profile edge should be off.
+ gl_FragData[0] = vec4(0.0, 1.0, 0.5, 0.0);
+ gl_FragData[1] = vec4(0.0, 0.5, 1.0, 0.0);
+ gl_FragData[2] = vec4(0.0, 1.0, 0.0, 0.0);
+ return;
+ }
+
+ // project
+ vec4 v0_pre_div = projection * ViewMatrix * vec4(v0_clipped_near, 1.0);
+ vec4 v1_pre_div = projection * ViewMatrix * vec4(v1_clipped_near, 1.0);
+
+ // perspective divide
+ vec3 v0_clip_pos = v0_pre_div.xyz;
+ vec3 v1_clip_pos = v1_pre_div.xyz;
+ if (is_perspective == 1) {
+ v0_clip_pos /= v0_pre_div.w;
+ v1_clip_pos /= v1_pre_div.w;
+ }
+ // clip to frustum
+ bool v0_on_screen = !pointOffScreen(v0_clip_pos);
+ bool v1_on_screen = !pointOffScreen(v1_clip_pos);
+
+ if (!v0_on_screen && !v1_on_screen) {
+ segment ret = clipSegmentBothOut(v0_clip_pos, v1_clip_pos);
+ if (ret.on_screen == false) {
+ // segment entirely off screen: BLUE / MAGENTA / BLACK
+ gl_FragData[0] = vec4(0.0, 0.0, 1.0, 0.0);
+ gl_FragData[1] = vec4(1.0, 0.0, 1.0, 0.0);
+ gl_FragData[2] = vec4(0.0, 0.0, 0.0, 0.0);
+ return;
+ }
+ v0_clip_pos = ret.p1;
+ v1_clip_pos = ret.p2;
+ }
+ else if (!v0_on_screen) {
+ v0_clip_pos = clipSegmentOneOut(v0_clip_pos, v1_clip_pos);
+ }
+ else if (!v1_on_screen) {
+ v1_clip_pos = clipSegmentOneOut(v1_clip_pos, v0_clip_pos);
+ }
+
+ // convert to window coordinates
+ vec2 v0_screen = (v0_clip_pos.xy + vec2(1.0, 1.0)) * 0.5 * viewport.zw;
+ vec2 v1_screen = (v1_clip_pos.xy + vec2(1.0, 1.0)) * 0.5 * viewport.zw;
+
+ float segment_screen_length = length(v0_screen - v1_screen);
+
+ // Unproject and reproject the final clipped positions
+ // so that interpolation is perspective correct later on.
+ vec4 v0_world = inverse_projection * vec4(v0_clip_pos, 1.0);
+ vec4 v1_world = inverse_projection * vec4(v1_clip_pos, 1.0);
+ vec4 v0_clipped_pre_div = projection * v0_world;
+ vec4 v1_clipped_pre_div = projection * v1_world;
+
+ gl_FragData[0] = vec4(v0_clipped_pre_div.xyz,
+ 1); // contour has priority, modification cause trouble
+ gl_FragData[1] = vec4(v1_clipped_pre_div.xyz, is_crease > 0 ? crease_strength : 1);
+}
diff --git a/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_passthrough_vert.glsl b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_passthrough_vert.glsl
new file mode 100644
index 00000000000..dabca7f56d3
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/shaders/lanpr_dpix_project_passthrough_vert.glsl
@@ -0,0 +1,6 @@
+in vec4 pos;
+
+void main()
+{
+ gl_Position = pos;
+}
diff --git a/source/blender/draw/engines/lanpr/shaders/lanpr_software_chain_geom.glsl b/source/blender/draw/engines/lanpr/shaders/lanpr_software_chain_geom.glsl
new file mode 100644
index 00000000000..92824d7708d
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/shaders/lanpr_software_chain_geom.glsl
@@ -0,0 +1,293 @@
+layout(lines_adjacency) in;
+layout(triangle_strip, max_vertices = 6) out;
+
+in vec2 gOffset[];
+in int gType[];
+in int gLevel[];
+
+in vec3 gNormal[];
+uniform int normal_mode;
+uniform int normal_effect_inverse;
+uniform vec3 normal_direction; // also used as point position
+uniform float normal_ramp_begin;
+uniform float normal_ramp_end;
+uniform float normal_thickness_start;
+uniform float normal_thickness_end;
+
+uniform float thickness;
+uniform float thickness_contour;
+uniform float thickness_crease;
+uniform float thickness_material;
+uniform float thickness_edge_mark;
+uniform float thickness_intersection;
+
+uniform int use_contour;
+uniform int use_crease;
+uniform int use_material;
+uniform int use_edge_mark;
+uniform int use_intersection;
+
+uniform int occlusion_level_start;
+uniform int occlusion_level_end;
+
+// implement these later.
+// uniform float depth_width_influence;
+// uniform float depth_width_curve;
+// uniform float depth_alpha_influence;
+// uniform float depth_alpha_curve;
+// uniform float zNear;
+// uniform float zFar;
+
+uniform vec4 color_contour;
+uniform vec4 color_crease;
+uniform vec4 color_material;
+uniform vec4 color_edge_mark;
+uniform vec4 color_intersection;
+
+uniform float taper_l_dist;
+uniform float taper_r_dist;
+uniform float taper_l_strength;
+uniform float taper_r_strength;
+
+// for line width correction
+uniform vec4 output_viewport;
+uniform vec4 preview_viewport;
+
+uniform float camdx;
+uniform float camdy;
+uniform float camzoom;
+
+out vec4 color_out;
+
+float use_thickness;
+
+#define M_PI 3.1415926535897932384626433832795
+
+#define LANPR_CHAIN_ENDPOINT_FLAG 3e30f
+
+vec4 END_POINT = vec4(vec2(LANPR_CHAIN_ENDPOINT_FLAG), 0, 1);
+
+vec4 MakeLeftTaperLinear(vec4 L, vec4 a, float offset)
+{
+ if (offset >= taper_l_dist)
+ return a;
+ a = mix(mix(a, L, taper_l_strength), a, offset / taper_l_dist);
+ return a;
+}
+
+vec4 MakeRightTaperLinear(vec4 R, vec4 c, float offset)
+{
+ if (offset >= taper_r_dist)
+ return c;
+ c = mix(mix(c, R, taper_r_strength), c, offset / taper_r_dist);
+ return c;
+}
+
+void draw_line(vec4 LL, vec4 L, vec4 R, vec4 RR)
+{
+
+ float LAngle, RAngle;
+
+ float OffsetL = gOffset[1].x;
+ float OffsetR = gOffset[2].x;
+ float OffsetL2 = gOffset[1].y;
+ float OffsetR2 = gOffset[2].y;
+
+ if (L == R)
+ return;
+
+ vec4 a;
+ vec4 b;
+ vec4 c;
+ vec4 d;
+ vec4 Line = R - L;
+ vec4 Normal = normalize(vec4(-Line.y, Line.x, 0, 0));
+
+ a = L - use_thickness * Normal * 0.001;
+ b = L + use_thickness * Normal * 0.001;
+ c = R - use_thickness * Normal * 0.001;
+ d = R + use_thickness * Normal * 0.001;
+
+ float lim = use_thickness * 0.002;
+
+ float x_scale = preview_viewport.w / preview_viewport.z;
+
+ if (LL.x < 3e20) {
+ vec4 avg = normalize(L - LL) + normalize(R - L);
+ if (length(avg) > 0.001) {
+ vec4 Tangent = normalize(avg);
+ vec4 Minter = normalize(vec4(-Tangent.y, Tangent.x, 0, 0));
+ float length = use_thickness / (dot(Minter, Normal)) * 0.001;
+ if (length < 4 * lim) {
+ Minter.x *= x_scale;
+ a = L - length * Minter;
+ b = L + length * Minter;
+ }
+ }
+ }
+
+ if (RR.x < 3e20) {
+ vec4 avg = normalize(RR - R) + normalize(R - L);
+ if (length(avg) > 0.001) {
+ vec4 Tangent = normalize(avg);
+ vec4 Minter = normalize(vec4(-Tangent.y, Tangent.x, 0, 0));
+ float length = use_thickness / (dot(Minter, Normal)) * 0.001;
+ if (length < 4 * lim) {
+ Minter.x *= x_scale;
+ c = R - length * Minter;
+ d = R + length * Minter;
+ }
+ }
+ }
+
+ a = MakeLeftTaperLinear(L, a, OffsetL);
+ b = MakeLeftTaperLinear(L, b, OffsetL);
+ c = MakeLeftTaperLinear(R, c, OffsetR);
+ d = MakeLeftTaperLinear(R, d, OffsetR);
+
+ a = MakeRightTaperLinear(L, a, OffsetL2);
+ b = MakeRightTaperLinear(L, b, OffsetL2);
+ c = MakeRightTaperLinear(R, c, OffsetR2);
+ d = MakeRightTaperLinear(R, d, OffsetR2);
+
+ a.w = 1;
+ b.w = 1;
+ c.w = 1;
+ d.w = 1;
+
+ gl_Position = a;
+ EmitVertex();
+ gl_Position = b;
+ EmitVertex();
+ gl_Position = c;
+ EmitVertex();
+ EndPrimitive();
+
+ gl_Position = c;
+ EmitVertex();
+ gl_Position = d;
+ EmitVertex();
+ gl_Position = b;
+ EmitVertex();
+ EndPrimitive();
+}
+
+float factor_to_thickness(float factor)
+{
+ float r = (factor - normal_ramp_begin) / (normal_ramp_end - normal_ramp_begin);
+ if (r > 1)
+ r = 1;
+ if (r < 0)
+ r = 0;
+ float thickness = normal_effect_inverse == 1 ?
+ mix(normal_thickness_start, normal_thickness_end, r) :
+ mix(normal_thickness_end, normal_thickness_start, r);
+ return thickness;
+}
+
+void decide_line_style(int component_id)
+{
+ float th = thickness;
+ if (normal_mode == 0) {
+ th = thickness;
+ }
+ else if (normal_mode == 1) {
+ float factor = dot(gNormal[0], normal_direction);
+ th = factor_to_thickness(factor);
+ }
+ else if (normal_mode == 2) {
+ float factor = dot(gNormal[0], normal_direction);
+ th = factor_to_thickness(factor);
+ }
+
+ if (component_id == 0) {
+ color_out = color_contour;
+ use_thickness = th * thickness_contour * use_contour;
+ return;
+ }
+ if (component_id == 1) {
+ color_out = color_crease;
+ use_thickness = th * thickness_crease * use_crease;
+ return;
+ }
+ if (component_id == 2) {
+ color_out = color_material;
+ use_thickness = th * thickness_material * use_material;
+ return;
+ }
+ if (component_id == 3) {
+ color_out = color_edge_mark;
+ use_thickness = th * thickness_edge_mark * use_edge_mark;
+ return;
+ }
+ if (component_id == 4) {
+ color_out = color_intersection;
+ use_thickness = th * thickness_intersection * use_intersection;
+ return;
+ }
+}
+
+vec4 correct_camera_scale(vec4 p)
+{
+
+ p.x -= camdx * 4;
+ p.y -= camdy * 4;
+ p.xy *= camzoom;
+ return p;
+}
+
+void main()
+{
+ int level = gLevel[1];
+
+ if (occlusion_level_start > level || occlusion_level_end < level)
+ return;
+
+ float asp1 = output_viewport.z / output_viewport.w;
+ float asp2 = preview_viewport.z / preview_viewport.w;
+ float x_scale;
+ float y_scale;
+ if (asp1 > 1) {
+ if (asp2 < 1) {
+ x_scale = 1 / asp2;
+ y_scale = 1 / asp1;
+ }
+ else {
+ x_scale = 1;
+ y_scale = asp2 / asp1;
+ }
+ }
+ else {
+ if (asp2 < 1) {
+ x_scale = asp1 / asp2;
+ y_scale = 1;
+ }
+ else {
+ x_scale = asp1;
+ y_scale = asp2;
+ }
+ }
+
+ vec4 LL = vec4(gl_in[0].gl_Position.xy, 0, 1), L = vec4(gl_in[1].gl_Position.xy, 0, 1),
+ R = vec4(gl_in[2].gl_Position.xy, 0, 1), RR = vec4(gl_in[3].gl_Position.xy, 0, 1);
+
+ LL.x *= x_scale;
+ LL.y *= y_scale;
+ L.x *= x_scale;
+ L.y *= y_scale;
+ R.x *= x_scale;
+ R.y *= y_scale;
+ RR.x *= x_scale;
+ RR.y *= y_scale;
+
+ LL = correct_camera_scale(LL);
+ L = correct_camera_scale(L);
+ R = correct_camera_scale(R);
+ RR = correct_camera_scale(RR);
+
+ int type = gType[1];
+
+ decide_line_style(type);
+
+ draw_line(LL, L, R, RR);
+}
diff --git a/source/blender/draw/engines/lanpr/shaders/lanpr_software_line_chain_geom.glsl b/source/blender/draw/engines/lanpr/shaders/lanpr_software_line_chain_geom.glsl
new file mode 100644
index 00000000000..efbd09f58f4
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/shaders/lanpr_software_line_chain_geom.glsl
@@ -0,0 +1,185 @@
+layout(lines) in;
+layout(triangle_strip, max_vertices = 6) out;
+
+in vec3 gNormal[];
+uniform int normal_mode;
+uniform int normal_effect_inverse;
+uniform vec3 normal_direction; // also used as point position
+uniform float normal_ramp_begin;
+uniform float normal_ramp_end;
+uniform float normal_thickness_start;
+uniform float normal_thickness_end;
+
+uniform float thickness;
+uniform float thickness_contour;
+uniform float thickness_crease;
+uniform float thickness_material;
+uniform float thickness_edge_mark;
+uniform float thickness_intersection;
+
+// implement these later.
+// uniform float depth_width_influence;
+// uniform float depth_width_curve;
+// uniform float depth_alpha_influence;
+// uniform float depth_alpha_curve;
+// uniform float zNear;
+// uniform float zFar;
+
+uniform vec4 color_contour;
+uniform vec4 color_crease;
+uniform vec4 color_material;
+uniform vec4 color_edge_mark;
+uniform vec4 color_intersection;
+
+// for line width correction
+uniform vec4 output_viewport;
+uniform vec4 preview_viewport;
+
+uniform float camdx;
+uniform float camdy;
+uniform float camzoom;
+
+out vec4 color_out;
+
+float use_thickness;
+
+void draw_line(vec4 p1, vec4 p2)
+{
+
+ vec4 Line = p2 - p1;
+ vec4 Normal = normalize(vec4(-Line.y, Line.x, 0, 0));
+
+ vec4 a, b, c, d;
+
+ float x_scale = preview_viewport.w / preview_viewport.z;
+ Normal.x *= x_scale;
+ vec4 offset = Normal * use_thickness * 0.001;
+
+ a = p1 + offset;
+ b = p1 - offset;
+ c = p2 + offset;
+ d = p2 - offset;
+
+ gl_Position = vec4(a.xy, 0, 1);
+ EmitVertex();
+ gl_Position = vec4(b.xy, 0, 1);
+ EmitVertex();
+ gl_Position = vec4(c.xy, 0, 1);
+ EmitVertex();
+
+ gl_Position = vec4(b.xy, 0, 1);
+ EmitVertex();
+ gl_Position = vec4(c.xy, 0, 1);
+ EmitVertex();
+ gl_Position = vec4(d.xy, 0, 1);
+ EmitVertex();
+
+ EndPrimitive();
+}
+
+float factor_to_thickness(float factor)
+{
+ float r = (factor - normal_ramp_begin) / (normal_ramp_end - normal_ramp_begin);
+ if (r > 1)
+ r = 1;
+ if (r < 0)
+ r = 0;
+ float thickness = normal_effect_inverse == 1 ?
+ mix(normal_thickness_start, normal_thickness_end, r) :
+ mix(normal_thickness_end, normal_thickness_start, r);
+ return thickness;
+}
+
+void decide_color_and_thickness(float component_id)
+{
+ float th = thickness;
+ if (normal_mode == 0) {
+ th = thickness;
+ }
+ else if (normal_mode == 1) {
+ float factor = dot(gNormal[0], normal_direction);
+ th = factor_to_thickness(factor);
+ }
+ else if (normal_mode == 2) {
+ float factor = dot(gNormal[0], normal_direction);
+ th = factor_to_thickness(factor);
+ }
+
+ if (component_id < 1.5) {
+ color_out = color_contour;
+ use_thickness = th * thickness_contour;
+ return;
+ }
+ if (component_id < 2.5) {
+ color_out = color_crease;
+ use_thickness = th * thickness_crease;
+ return;
+ }
+ if (component_id < 3.5) {
+ color_out = color_material;
+ use_thickness = th * thickness_material;
+ return;
+ }
+ if (component_id < 4.5) {
+ color_out = color_edge_mark;
+ use_thickness = th * thickness_edge_mark;
+ return;
+ }
+ if (component_id < 5.5) {
+ color_out = color_intersection;
+ use_thickness = th * thickness_intersection;
+ return;
+ }
+}
+
+vec4 correct_camera_scale(vec4 p)
+{
+ p.x -= camdx * 4;
+ p.y -= camdy * 4;
+ p.xy *= camzoom;
+ return p;
+}
+
+void main()
+{
+
+ float asp1 = output_viewport.z / output_viewport.w;
+ float asp2 = preview_viewport.z / preview_viewport.w;
+ float x_scale;
+ float y_scale;
+ if (asp1 > 1) {
+ if (asp2 < 1) {
+ x_scale = 1 / asp2;
+ y_scale = 1 / asp1;
+ }
+ else {
+ x_scale = 1;
+ y_scale = asp2 / asp1;
+ }
+ }
+ else {
+ if (asp2 < 1) {
+ x_scale = asp1 / asp2;
+ y_scale = 1;
+ }
+ else {
+ x_scale = asp1;
+ y_scale = asp2;
+ }
+ }
+
+ vec4 p1 = vec4(gl_in[0].gl_Position.xy, 0, 1);
+ vec4 p2 = vec4(gl_in[1].gl_Position.xy, 0, 1);
+
+ p1.x *= x_scale;
+ p2.x *= x_scale;
+ p1.y *= y_scale;
+ p2.y *= y_scale;
+
+ p1 = correct_camera_scale(p1);
+ p2 = correct_camera_scale(p2);
+
+ decide_color_and_thickness(gl_in[0].gl_Position.z);
+
+ draw_line(p1, p2);
+}
diff --git a/source/blender/draw/engines/lanpr/shaders/lanpr_software_passthrough_vert.glsl b/source/blender/draw/engines/lanpr/shaders/lanpr_software_passthrough_vert.glsl
new file mode 100644
index 00000000000..e802bcbc582
--- /dev/null
+++ b/source/blender/draw/engines/lanpr/shaders/lanpr_software_passthrough_vert.glsl
@@ -0,0 +1,21 @@
+in vec4 pos;
+in vec2 uvs;
+in vec3 normal;
+in int type;
+in int level;
+
+out vec2 gOffset;
+out int gType;
+out int gLevel;
+out vec3 gNormal;
+
+void main()
+{
+ vec4 p = pos;
+
+ gOffset = uvs;
+ gType = type;
+ gLevel = level;
+ gNormal = normal;
+ gl_Position = vec4(vec3(p), 1);
+}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index e6e66eea32f..d3585b02b7d 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -175,6 +175,17 @@ void DRW_texture_ensure_fullscreen_2d(struct GPUTexture **tex,
void DRW_texture_ensure_2d(
struct GPUTexture **tex, int w, int h, eGPUTextureFormat format, DRWTextureFlag flags);
+void DRW_texture_ensure_fullscreen_2D_multisample(struct GPUTexture **tex,
+ eGPUTextureFormat format,
+ int samples,
+ DRWTextureFlag flags);
+void DRW_texture_ensure_2D_multisample(struct GPUTexture **tex,
+ int w,
+ int h,
+ eGPUTextureFormat format,
+ int samples,
+ DRWTextureFlag flags);
+
void DRW_texture_generate_mipmaps(struct GPUTexture *tex);
void DRW_texture_free(struct GPUTexture *tex);
#define DRW_TEXTURE_FREE_SAFE(tex) \
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 970578c7438..bdef6d50db8 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -96,6 +96,10 @@
#include "engines/select/select_engine.h"
#include "engines/workbench/workbench_engine.h"
+#ifdef WITH_LANPR
+# include "engines/lanpr/lanpr_all.h"
+#endif
+
#include "GPU_context.h"
#include "DEG_depsgraph.h"
@@ -2642,6 +2646,9 @@ void DRW_engines_register(void)
{
RE_engines_register(&DRW_engine_viewport_eevee_type);
RE_engines_register(&DRW_engine_viewport_workbench_type);
+#ifdef WITH_LANPR
+ RE_engines_register(&DRW_engine_viewport_lanpr_type);
+#endif
DRW_engine_register(&draw_engine_gpencil_type);
diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c
index 810a2e9389b..6abab38e1e6 100644
--- a/source/blender/draw/intern/draw_manager_texture.c
+++ b/source/blender/draw/intern/draw_manager_texture.c
@@ -168,6 +168,30 @@ void DRW_texture_ensure_2d(
}
}
+void DRW_texture_ensure_fullscreen_2D_multisample(GPUTexture **tex,
+ eGPUTextureFormat format,
+ int samples,
+ DRWTextureFlag UNUSED(flags))
+{
+ if (*(tex) == NULL) {
+ const float *size = DRW_viewport_size_get();
+ *(tex) = GPU_texture_create_2d_multisample(
+ (int)size[0], (int)size[1], format, NULL, samples, NULL);
+ }
+}
+
+void DRW_texture_ensure_2D_multisample(GPUTexture **tex,
+ int w,
+ int h,
+ eGPUTextureFormat format,
+ int samples,
+ DRWTextureFlag UNUSED(flags))
+{
+ if (*(tex) == NULL) {
+ *(tex) = GPU_texture_create_2d_multisample(w, h, format, NULL, samples, NULL);
+ }
+}
+
void DRW_texture_generate_mipmaps(GPUTexture *tex)
{
GPU_texture_bind(tex, 0);
diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt
index 7910cf47a33..191a017ecce 100644
--- a/source/blender/editors/CMakeLists.txt
+++ b/source/blender/editors/CMakeLists.txt
@@ -64,3 +64,8 @@ if(WITH_BLENDER)
endif()
add_subdirectory(datafiles)
+
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+ add_subdirectory(lanpr)
+endif()
diff --git a/source/blender/editors/include/ED_lanpr.h b/source/blender/editors/include/ED_lanpr.h
new file mode 100644
index 00000000000..497085c2d6c
--- /dev/null
+++ b/source/blender/editors/include/ED_lanpr.h
@@ -0,0 +1,625 @@
+/*
+ * 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
+ */
+
+#ifndef __ED_LANPR_H__
+#define __ED_LANPR_H__
+
+#ifndef WITH_LANPR
+# error LANPR code included in non-LANPR-enabled build
+#endif
+
+#include "BLI_listbase.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_threads.h"
+
+#include "DNA_lanpr_types.h"
+
+#include <math.h>
+#include <string.h>
+
+typedef struct LANPR_StaticMemPoolNode {
+ Link item;
+ int used_byte;
+ /* User memory starts here */
+} LANPR_StaticMemPoolNode;
+
+typedef struct LANPR_StaticMemPool {
+ int each_size;
+ ListBase pools;
+ SpinLock lock_mem;
+} LANPR_StaticMemPool;
+
+typedef struct LANPR_RenderTriangle {
+ struct LANPR_RenderTriangle *next, *prev;
+ struct LANPR_RenderVert *v[3];
+ struct LANPR_RenderLine *rl[3];
+ double gn[3];
+ double gc[3];
+ /* struct BMFace *F; */
+ short material_id;
+ ListBase intersecting_verts;
+ char cull_status;
+ /** Should be testing** , Use testing[NumOfThreads] to access. */
+ struct LANPR_RenderTriangle *testing;
+} LANPR_RenderTriangle;
+
+typedef struct LANPR_RenderTriangleThread {
+ struct LANPR_RenderTriangle base;
+ struct LANPR_RenderLine *testing[127];
+} LANPR_RenderTriangleThread;
+
+typedef struct LANPR_RenderElementLinkNode {
+ struct LANPR_RenderElementLinkNode *next, *prev;
+ void *pointer;
+ int element_count;
+ void *object_ref;
+ char additional;
+} LANPR_RenderElementLinkNode;
+
+typedef struct LANPR_RenderLineSegment {
+ struct LANPR_RenderLineSegment *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. (TODO: implement this) */
+ short material_mask_mark;
+} LANPR_RenderLineSegment;
+
+typedef struct LANPR_RenderVert {
+ struct LANPR_RenderVert *next, *prev;
+ double gloc[4];
+ double fbcoord[4];
+ int fbcoordi[2];
+ /** Used as "r" when intersecting */
+ struct BMVert *v;
+ struct LANPR_RenderLine *intersecting_line;
+ struct LANPR_RenderLine *intersecting_line2;
+ struct LANPR_RenderTriangle *intersecting_with;
+
+ /** This will used in future acceleration for intersection processing. */
+ char edge_used;
+} LANPR_RenderVert;
+
+typedef enum LANPR_EdgeFlag {
+ LANPR_EDGE_FLAG_EDGE_MARK = (1 << 0),
+ LANPR_EDGE_FLAG_CONTOUR = (1 << 1),
+ LANPR_EDGE_FLAG_CREASE = (1 << 2),
+ LANPR_EDGE_FLAG_MATERIAL = (1 << 3),
+ LANPR_EDGE_FLAG_INTERSECTION = (1 << 4),
+ /** floating edge, unimplemented yet */
+ LANPR_EDGE_FLAG_FLOATING = (1 << 5),
+ LANPR_EDGE_FLAG_CHAIN_PICKED = (1 << 6),
+} LANPR_EdgeFlag;
+
+#define LANPR_EDGE_FLAG_ALL_TYPE 0x3f
+
+typedef struct LANPR_RenderLine {
+ struct LANPR_RenderLine *next, *prev;
+ struct LANPR_RenderVert *l, *r;
+ struct LANPR_RenderTriangle *tl, *tr;
+ ListBase segments;
+ char min_occ;
+
+ /** Also for line type determination on chainning */
+ char flags;
+
+ /** Still need this entry because culled lines will not add to object reln node */
+ struct Object *object_ref;
+
+ /** For gpencil stroke modifier */
+ int edge_idx;
+} LANPR_RenderLine;
+
+typedef struct LANPR_RenderLineChain {
+ struct LANPR_RenderLineChain *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;
+ struct Object *object_ref;
+} LANPR_RenderLineChain;
+
+typedef struct LANPR_RenderLineChainItem {
+ struct LANPR_RenderLineChainItem *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;
+} LANPR_RenderLineChainItem;
+
+typedef struct LANPR_ChainRegisterEntry {
+ struct LANPR_ChainRegisterEntry *next, *prev;
+ LANPR_RenderLineChain *rlc;
+ LANPR_RenderLineChainItem *rlci;
+ char picked;
+
+ /** left/right mark.
+ * Because we revert list in chaining so we need the flag. */
+ char is_left;
+} LANPR_ChainRegisterEntry;
+
+typedef struct LANPR_RenderBuffer {
+ struct LANPR_RenderBuffer *prev, *next;
+
+ /** For render. */
+ int is_copied;
+
+ int w, h;
+ int tile_size_w, tile_size_h;
+ int tile_count_x, tile_count_y;
+ double width_per_tile, height_per_tile;
+ double view_projection[4][4];
+
+ int output_mode;
+ int output_aa_level;
+
+ struct LANPR_BoundingArea *initial_bounding_areas;
+ unsigned int bounding_area_count;
+
+ ListBase vertex_buffer_pointers;
+ ListBase line_buffer_pointers;
+ ListBase triangle_buffer_pointers;
+ ListBase all_render_lines;
+
+ ListBase intersecting_vertex_buffer;
+
+ struct GPUBatch *DPIXIntersectionTransformBatch;
+ struct GPUBatch *DPIXIntersectionBatch;
+
+ /** Use the one comes with LANPR. */
+ LANPR_StaticMemPool render_data_pool;
+
+ struct Material *material_pointers[2048];
+
+ /* Render status */
+ double view_vector[3];
+
+ int triangle_size;
+
+ unsigned int contour_count;
+ unsigned int contour_processed;
+ LinkData *contour_managed;
+ ListBase contours;
+
+ unsigned int intersection_count;
+ unsigned int intersection_processed;
+ LinkData *intersection_managed;
+ ListBase intersection_lines;
+
+ unsigned int crease_count;
+ unsigned int crease_processed;
+ LinkData *crease_managed;
+ ListBase crease_lines;
+
+ unsigned int material_line_count;
+ unsigned int material_processed;
+ LinkData *material_managed;
+ ListBase material_lines;
+
+ unsigned int edge_mark_count;
+ unsigned int edge_mark_processed;
+ LinkData *edge_mark_managed;
+ ListBase edge_marks;
+
+ ListBase chains;
+ struct GPUBatch *chain_draw_batch;
+
+ struct DRWShadingGroup *chain_shgrp;
+
+ /** For managing calculation tasks for multiple threads. */
+ SpinLock lock_task;
+
+ /* settings */
+
+ int max_occlusion_level;
+ double crease_angle;
+ double crease_cos;
+ int thread_count;
+
+ int draw_material_preview;
+ double material_transparency;
+
+ int show_line;
+ int show_fast;
+ int show_material;
+ int override_display;
+
+ int use_intersections;
+ int _pad;
+
+ /** Keep an copy of these data so the scene can be freed when lanpr is runnning. */
+ char cam_is_persp;
+ float cam_obmat[4][4];
+ double camera_pos[3];
+ double near_clip, far_clip;
+ double shift_x, shift_y;
+ double chaining_image_threshold;
+ double chaining_geometry_threshold;
+} LANPR_RenderBuffer;
+
+typedef enum LANPR_RenderStatus {
+ LANPR_RENDER_IDLE = 0,
+ LANPR_RENDER_RUNNING = 1,
+ LANPR_RENDER_INCOMPELTE = 2,
+ LANPR_RENDER_FINISHED = 3,
+} LANPR_RenderStatus;
+
+typedef enum LANPR_INIT_STATUS {
+ LANPR_INIT_ENGINE = (1 << 0),
+ LANPR_INIT_LOCKS = (1 << 1),
+} LANPR_INIT_STATUS;
+
+typedef struct LANPR_SharedResource {
+
+ /* We only allocate once for all */
+ LANPR_RenderBuffer *render_buffer_shared;
+
+ /* cache */
+ struct BLI_mempool *mp_sample;
+ struct BLI_mempool *mp_line_strip;
+ struct BLI_mempool *mp_line_strip_point;
+ struct BLI_mempool *mp_batch_list;
+
+ /* Shared */
+ struct GPUShader *multichannel_shader;
+
+ /* GPU */
+ struct GPUShader *dpix_transform_shader;
+ struct GPUShader *dpix_preview_shader;
+ bool dpix_shader_error;
+ int texture_size;
+ ListBase dpix_batch_list;
+ int dpix_reloaded;
+ int dpix_reloaded_deg;
+
+ /* CPU */
+ struct GPUShader *software_shader;
+ struct GPUShader *software_chaining_shader;
+
+ struct TaskPool *background_render_task;
+
+ void *ved_viewport;
+ void *ved_render;
+
+ LANPR_INIT_STATUS init_complete;
+
+ /** To bypass or cancel rendering.
+ * This status flag should be kept in lanpr_share not render_buffer,
+ * because render_buffer will get re-initialized every frame.
+ */
+ SpinLock lock_render_status;
+ LANPR_RenderStatus flag_render_status;
+
+ /** Geometry loading is done in the worker thread,
+ * Lock the render thread until loading is done, so that
+ * we can avoid depsgrapgh deleting the scene before
+ * LANPR finishes loading. Also keep this in lanpr_share.
+ */
+ SpinLock lock_loader;
+
+ /** Set before rendering and cleared upon finish! */
+ struct RenderEngine *re_render;
+
+ /** When drawing in the viewport, use the following values. */
+ /** Set to override to -1 before creating lanpr render buffer to use scene camera. */
+ int viewport_camera_override;
+ char camera_is_persp;
+ float camera_pos[3];
+ float near_clip, far_clip;
+ float viewinv[4][4];
+ float persp[4][4];
+ float viewquat[4];
+} LANPR_SharedResource;
+
+#define DBL_TRIANGLE_LIM 1e-8
+#define DBL_EDGE_LIM 1e-9
+
+#define LANPR_MEMORY_POOL_1MB 1048576
+#define LANPR_MEMORY_POOL_128MB 134217728
+#define LANPR_MEMORY_POOL_256MB 268435456
+#define LANPR_MEMORY_POOL_512MB 536870912
+
+typedef enum LANPR_CullState {
+ LANPR_CULL_DONT_CARE = 0,
+ LANPR_CULL_USED = 1,
+ LANPR_CULL_DISCARD = 2,
+} LANPR_CullState;
+
+/** 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 TNS_THREAD_LINE_COUNT 10000
+
+typedef struct LANPR_RenderTaskInfo {
+ int thread_id;
+
+ LinkData *contour;
+ ListBase contour_pointers;
+
+ LinkData *intersection;
+ ListBase intersection_pointers;
+
+ LinkData *crease;
+ ListBase crease_pointers;
+
+ LinkData *material;
+ ListBase material_pointers;
+
+ LinkData *edge_mark;
+ ListBase edge_mark_pointers;
+
+} LANPR_RenderTaskInfo;
+
+/** 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 LANPR_BoundingArea {
+ double l, r, u, b;
+ double cx, cy;
+
+ /** 1,2,3,4 quadrant */
+ struct LANPR_BoundingArea *child;
+
+ ListBase lp;
+ ListBase rp;
+ ListBase up;
+ ListBase bp;
+
+ int triangle_count;
+ ListBase linked_triangles;
+ ListBase linked_lines;
+
+ /** Reserved for image space reduction && multithread chainning */
+ ListBase linked_chains;
+} LANPR_BoundingArea;
+
+#define TNS_COMMAND_LINE 0
+#define TNS_COMMAND_MATERIAL 1
+#define TNS_COMMAND_EDGE 2
+
+#define TNS_TRANSPARENCY_DRAW_SIMPLE 0
+#define TNS_TRANSPARENCY_DRAW_LAYERED 1
+
+#define TNS_OVERRIDE_ONLY 0
+#define TNS_OVERRIDE_EXCLUDE 1
+/* #define TNS_OVERRIDE_ALL_OTHERS_OUTSIDE_GROUP 2 */
+/* #define TNS_OVERRIDE_ALL_OTHERS_IN_GROUP 3 */
+/* #define TNS_OVERRIDE_ALL_OTHERS 4 */
+
+#define TNS_TILE(tile, r, c, CCount) tile[r * CCount + c]
+
+#define TNS_CLAMP(a, Min, Max) a = a < Min ? Min : (a > Max ? Max : a)
+
+#define TNS_MAX3_INDEX(a, b, c) (a > b ? (a > c ? 0 : (b > c ? 1 : 2)) : (b > c ? 1 : 2))
+
+#define TNS_MIN3_INDEX(a, b, c) (a < b ? (a < c ? 0 : (b < c ? 1 : 2)) : (b < c ? 1 : 2))
+
+#define TNS_MAX3_INDEX_ABC(x, y, z) (x > y ? (x > z ? a : (y > z ? b : c)) : (y > z ? b : c))
+
+#define TNS_MIN3_INDEX_ABC(x, y, z) (x < y ? (x < z ? a : (y < z ? b : c)) : (y < z ? b : c))
+
+#define TNS_ABC(index) (index == 0 ? a : (index == 1 ? b : c))
+
+#define TNS_DOUBLE_CLOSE_ENOUGH(a, b) (((a) + DBL_EDGE_LIM) >= (b) && ((a)-DBL_EDGE_LIM) <= (b))
+
+/* #define TNS_DOUBLE_CLOSE_ENOUGH(a,b)\ */
+/* //(((a)+0.00000000001)>=(b) && ((a)-0.0000000001)<=(b)) */
+
+#define TNS_FLOAT_CLOSE_ENOUGH_WIDER(a, b) (((a) + 0.0000001) >= (b) && ((a)-0.0000001) <= (b))
+
+#define TNS_IN_TILE_X(RenderTile, Fx) (RenderTile->FX <= Fx && RenderTile->FXLim >= Fx)
+
+#define TNS_IN_TILE_Y(RenderTile, Fy) (RenderTile->FY <= Fy && RenderTile->FYLim >= Fy)
+
+#define TNS_IN_TILE(RenderTile, Fx, Fy) \
+ (TNS_IN_TILE_X(RenderTile, Fx) && TNS_IN_TILE_Y(RenderTile, Fy))
+
+BLI_INLINE int lanpr_TrangleLineBoundBoxTest(LANPR_RenderTriangle *rt, LANPR_RenderLine *rl)
+{
+ if (MAX3(rt->v[0]->fbcoord[2], rt->v[1]->fbcoord[2], rt->v[2]->fbcoord[2]) >
+ MIN2(rl->l->fbcoord[2], rl->r->fbcoord[2]))
+ return 0;
+ if (MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]) <
+ MIN2(rl->l->fbcoord[0], rl->r->fbcoord[0]))
+ return 0;
+ if (MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]) >
+ MAX2(rl->l->fbcoord[0], rl->r->fbcoord[0]))
+ return 0;
+ if (MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]) <
+ MIN2(rl->l->fbcoord[1], rl->r->fbcoord[1]))
+ return 0;
+ if (MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]) >
+ MAX2(rl->l->fbcoord[1], rl->r->fbcoord[1]))
+ return 0;
+ return 1;
+}
+
+BLI_INLINE double tmat_get_linear_ratio(double l, double r, double from_l);
+BLI_INLINE int lanpr_LineIntersectTest2d(
+ const double *a1, const double *a2, const double *b1, const double *b2, double *aRatio)
+{
+ double k1, k2;
+ double x;
+ double y;
+ double ratio;
+ double x_diff = (a2[0] - a1[0]);
+ double x_diff2 = (b2[0] - b1[0]);
+
+ if (x_diff == 0) {
+ if (x_diff2 == 0) {
+ *aRatio = 0;
+ return 0;
+ }
+ double r2 = tmat_get_linear_ratio(b1[0], b2[0], a1[0]);
+ x = interpd(b2[0], b1[0], r2);
+ y = interpd(b2[1], b1[1], r2);
+ *aRatio = ratio = tmat_get_linear_ratio(a1[1], a2[1], y);
+ }
+ else {
+ if (x_diff2 == 0) {
+ ratio = tmat_get_linear_ratio(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 (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;
+}
+
+BLI_INLINE double tmat_get_linear_ratio(double l, double r, double from_l)
+{
+ double ra = (from_l - l) / (r - l);
+ return ra;
+}
+
+int ED_lanpr_point_inside_triangled(double v[2], double v0[2], double v1[2], double v2[2]);
+
+struct Depsgraph;
+struct SceneLANPR;
+
+int ED_lanpr_object_collection_usage_check(struct Collection *c, struct Object *o);
+
+void ED_lanpr_NO_THREAD_chain_feature_lines(LANPR_RenderBuffer *rb);
+void ED_lanpr_split_chains_for_fixed_occlusion(LANPR_RenderBuffer *rb);
+void ED_lanpr_connect_chains(LANPR_RenderBuffer *rb, const int do_geometry_space);
+void ED_lanpr_discard_short_chains(LANPR_RenderBuffer *rb, const float threshold);
+int ED_lanpr_count_chain(const LANPR_RenderLineChain *rlc);
+void ED_lanpr_chain_clear_picked_flag(struct LANPR_RenderBuffer *rb);
+
+int ED_lanpr_count_leveled_edge_segment_count(const ListBase *line_list,
+ const struct LANPR_LineLayer *ll);
+void *ED_lanpr_make_leveled_edge_vertex_array(struct LANPR_RenderBuffer *rb,
+ const ListBase *line_list,
+ float *vertex_array,
+ float *normal_array,
+ float **next_normal,
+ const LANPR_LineLayer *ll,
+ const float componet_id);
+
+void ED_lanpr_calculation_set_flag(LANPR_RenderStatus flag);
+bool ED_lanpr_calculation_flag_check(LANPR_RenderStatus flag);
+
+int ED_lanpr_compute_feature_lines_internal(struct Depsgraph *depsgraph,
+ const int instersections_only);
+
+void ED_lanpr_compute_feature_lines_background(struct Depsgraph *dg, const int intersection_only);
+
+struct Scene;
+
+LANPR_RenderBuffer *ED_lanpr_create_render_buffer(struct Scene *s);
+void ED_lanpr_destroy_render_data(struct LANPR_RenderBuffer *rb);
+
+bool ED_lanpr_dpix_shader_error(void);
+
+void ED_lanpr_render_buffer_cache_free(struct LANPR_RenderBuffer *rb);
+
+int ED_lanpr_max_occlusion_in_line_layers(struct SceneLANPR *lanpr);
+LANPR_LineLayer *ED_lanpr_new_line_layer(struct SceneLANPR *lanpr);
+
+LANPR_BoundingArea *ED_lanpr_get_point_bounding_area(LANPR_RenderBuffer *rb, double x, double y);
+LANPR_BoundingArea *ED_lanpr_get_point_bounding_area_deep(LANPR_RenderBuffer *rb,
+ double x,
+ double y);
+
+void ED_lanpr_post_frame_update_external(struct Scene *s, struct Depsgraph *dg);
+
+struct SceneLANPR;
+
+void ED_lanpr_rebuild_all_command(struct Scene *s);
+
+void ED_lanpr_update_render_progress(const char *text);
+
+void ED_lanpr_calculate_normal_object_vector(LANPR_LineLayer *ll, float *normal_object_direction);
+
+float ED_lanpr_compute_chain_length(LANPR_RenderLineChain *rlc);
+
+struct wmOperatorType;
+
+/* Operator types */
+void SCENE_OT_lanpr_calculate_feature_lines(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_add_line_layer(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_delete_line_layer(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_rebuild_all_commands(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_auto_create_line_layer(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_move_line_layer(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_enable_all_line_types(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_update_gp_strokes(struct wmOperatorType *ot);
+void SCENE_OT_lanpr_bake_gp_strokes(struct wmOperatorType *ot);
+
+void OBJECT_OT_lanpr_update_gp_target(struct wmOperatorType *ot);
+void OBJECT_OT_lanpr_update_gp_source(struct wmOperatorType *ot);
+
+void ED_operatortypes_lanpr(void);
+
+#endif /* __ED_LANPR_H__ */
diff --git a/source/blender/editors/lanpr/CMakeLists.txt b/source/blender/editors/lanpr/CMakeLists.txt
new file mode 100644
index 00000000000..3c1b0b389f6
--- /dev/null
+++ b/source/blender/editors/lanpr/CMakeLists.txt
@@ -0,0 +1,46 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ ../include
+ ../../blenkernel
+ ../../blenlib
+ ../../bmesh
+ ../../depsgraph
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+ lanpr_ops.c
+ lanpr_cpu.c
+ lanpr_chain.c
+ lanpr_util.c
+
+ lanpr_intern.h
+)
+
+set(LIB
+)
+
+blender_add_lib(bf_editor_lanpr "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/lanpr/lanpr_chain.c b/source/blender/editors/lanpr/lanpr_chain.c
new file mode 100644
index 00000000000..91e957fd354
--- /dev/null
+++ b/source/blender/editors/lanpr/lanpr_chain.c
@@ -0,0 +1,759 @@
+/*
+ * 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_listbase.h"
+#include "BLI_linklist.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_lanpr_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_scene_types.h"
+
+#include "ED_lanpr.h"
+
+#include "bmesh.h"
+
+#include "lanpr_intern.h"
+
+#include <math.h>
+
+#define LANPR_OTHER_RV(rl, rv) ((rv) == (rl)->l ? (rl)->r : (rl)->l)
+
+static LANPR_RenderLine *lanpr_get_connected_render_line(LANPR_BoundingArea *ba,
+ LANPR_RenderVert *rv,
+ LANPR_RenderVert **new_rv,
+ int match_flag)
+{
+ LinkData *lip;
+ LANPR_RenderLine *nrl;
+
+ for (lip = ba->linked_lines.first; lip; lip = lip->next) {
+ nrl = lip->data;
+
+ if ((!(nrl->flags & LANPR_EDGE_FLAG_ALL_TYPE)) ||
+ (nrl->flags & LANPR_EDGE_FLAG_CHAIN_PICKED)) {
+ continue;
+ }
+
+ if (match_flag && ((nrl->flags & LANPR_EDGE_FLAG_ALL_TYPE) & match_flag) == 0) {
+ continue;
+ }
+
+ /* always chain connected lines for now. */
+ /* simplification will take care of the sharp points. */
+ /* if(cosine whatever) continue; */
+
+ if (rv != nrl->l && rv != nrl->r) {
+ if (nrl->flags & LANPR_EDGE_FLAG_INTERSECTION) {
+ if (rv->fbcoord[0] == nrl->l->fbcoord[0] && rv->fbcoord[1] == nrl->l->fbcoord[1]) {
+ *new_rv = LANPR_OTHER_RV(nrl, nrl->l);
+ return nrl;
+ }
+ else {
+ if (rv->fbcoord[0] == nrl->r->fbcoord[0] && rv->fbcoord[1] == nrl->r->fbcoord[1]) {
+ *new_rv = LANPR_OTHER_RV(nrl, nrl->r);
+ return nrl;
+ }
+ }
+ }
+ continue;
+ }
+
+ *new_rv = LANPR_OTHER_RV(nrl, rv);
+ return nrl;
+ }
+
+ return 0;
+}
+
+static LANPR_RenderLineChain *lanpr_create_render_line_chain(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderLineChain *rlc;
+ rlc = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineChain));
+
+ BLI_addtail(&rb->chains, rlc);
+
+ return rlc;
+}
+
+static LANPR_RenderLineChainItem *lanpr_append_render_line_chain_point(LANPR_RenderBuffer *rb,
+ LANPR_RenderLineChain *rlc,
+ float x,
+ float y,
+ float gx,
+ float gy,
+ float gz,
+ float *normal,
+ char type,
+ int level)
+{
+ LANPR_RenderLineChainItem *rlci;
+ rlci = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineChainItem));
+
+ rlci->pos[0] = x;
+ rlci->pos[1] = y;
+ rlci->gpos[0] = gx;
+ rlci->gpos[1] = gy;
+ rlci->gpos[2] = gz;
+ copy_v3_v3(rlci->normal, normal);
+ rlci->line_type = type & LANPR_EDGE_FLAG_ALL_TYPE;
+ rlci->occlusion = level;
+ BLI_addtail(&rlc->chain, rlci);
+
+ /* printf("a %f,%f %d\n", x, y, level); */
+
+ return rlci;
+}
+
+static LANPR_RenderLineChainItem *lanpr_push_render_line_chain_point(LANPR_RenderBuffer *rb,
+ LANPR_RenderLineChain *rlc,
+ float x,
+ float y,
+ float gx,
+ float gy,
+ float gz,
+ float *normal,
+ char type,
+ int level)
+{
+ LANPR_RenderLineChainItem *rlci;
+ rlci = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineChainItem));
+
+ rlci->pos[0] = x;
+ rlci->pos[1] = y;
+ rlci->gpos[0] = gx;
+ rlci->gpos[1] = gy;
+ rlci->gpos[2] = gz;
+ copy_v3_v3(rlci->normal, normal);
+ rlci->line_type = type & LANPR_EDGE_FLAG_ALL_TYPE;
+ rlci->occlusion = level;
+ BLI_addhead(&rlc->chain, rlci);
+
+ /* printf("data %f,%f %d\n", x, y, level); */
+
+ return rlci;
+}
+
+void ED_lanpr_NO_THREAD_chain_feature_lines(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderLineChain *rlc;
+ LANPR_RenderLineChainItem *rlci;
+ LANPR_RenderLine *rl;
+ LANPR_BoundingArea *ba;
+ LANPR_RenderLineSegment *rls;
+ int last_occlusion;
+
+ for (rl = rb->all_render_lines.first; rl; rl = rl->next) {
+
+ if ((!(rl->flags & LANPR_EDGE_FLAG_ALL_TYPE)) || (rl->flags & LANPR_EDGE_FLAG_CHAIN_PICKED)) {
+ continue;
+ }
+
+ rl->flags |= LANPR_EDGE_FLAG_CHAIN_PICKED;
+
+ rlc = lanpr_create_render_line_chain(rb);
+
+ rlc->object_ref = rl->object_ref; /* can only be the same object in a chain. */
+ rlc->type = (rl->flags & LANPR_EDGE_FLAG_ALL_TYPE);
+
+ LANPR_RenderLine *new_rl = rl;
+ LANPR_RenderVert *new_rv;
+ float N[3] = {0};
+
+ if (rl->tl) {
+ N[0] += rl->tl->gn[0];
+ N[1] += rl->tl->gn[1];
+ N[2] += rl->tl->gn[2];
+ }
+ if (rl->tr) {
+ N[0] += rl->tr->gn[0];
+ N[1] += rl->tr->gn[1];
+ N[2] += rl->tr->gn[2];
+ }
+ if (rl->tl || rl->tr) {
+ normalize_v3(N);
+ }
+
+ /* step 1: grow left */
+ ba = ED_lanpr_get_point_bounding_area_deep(rb, rl->l->fbcoord[0], rl->l->fbcoord[1]);
+ new_rv = rl->l;
+ rls = rl->segments.first;
+ lanpr_push_render_line_chain_point(rb,
+ rlc,
+ new_rv->fbcoord[0],
+ new_rv->fbcoord[1],
+ new_rv->gloc[0],
+ new_rv->gloc[1],
+ new_rv->gloc[2],
+ N,
+ rl->flags,
+ rls->occlusion);
+ while (ba && (new_rl = lanpr_get_connected_render_line(ba, new_rv, &new_rv, rl->flags))) {
+ new_rl->flags |= LANPR_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];
+ 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, rls->at);
+ lanpr_push_render_line_chain_point(rb,
+ rlc,
+ lpos[0],
+ lpos[1],
+ gpos[0],
+ gpos[1],
+ gpos[2],
+ N,
+ new_rl->flags,
+ rls->occlusion);
+ last_occlusion = rls->occlusion;
+ }
+ }
+ else if (new_rv == new_rl->r) {
+ rls = new_rl->segments.first;
+ last_occlusion = rls->occlusion;
+ rls = rls->next;
+ for (; rls; rls = rls->next) {
+ double gpos[3], lpos[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, rls->at);
+ lanpr_push_render_line_chain_point(rb,
+ rlc,
+ lpos[0],
+ lpos[1],
+ gpos[0],
+ gpos[1],
+ gpos[2],
+ N,
+ new_rl->flags,
+ last_occlusion);
+ last_occlusion = rls->occlusion;
+ }
+ lanpr_push_render_line_chain_point(rb,
+ rlc,
+ new_rl->r->fbcoord[0],
+ new_rl->r->fbcoord[1],
+ new_rl->r->gloc[0],
+ new_rl->r->gloc[1],
+ new_rl->r->gloc[2],
+ N,
+ new_rl->flags,
+ last_occlusion);
+ }
+ ba = ED_lanpr_get_point_bounding_area_deep(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ }
+
+ /* Restore normal value */
+ if (rl->tl || rl->tr) {
+ zero_v3(N);
+ if (rl->tl) {
+ N[0] += rl->tl->gn[0];
+ N[1] += rl->tl->gn[1];
+ N[2] += rl->tl->gn[2];
+ }
+ if (rl->tr) {
+ N[0] += rl->tr->gn[0];
+ N[1] += rl->tr->gn[1];
+ N[2] += rl->tr->gn[2];
+ }
+ normalize_v3(N);
+ }
+ /* step 2: this line */
+ rls = rl->segments.first;
+ last_occlusion = ((LANPR_RenderLineSegment *)rls)->occlusion;
+ for (rls = rls->next; rls; rls = rls->next) {
+ double gpos[3], lpos[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, rls->at);
+ lanpr_append_render_line_chain_point(
+ rb, rlc, lpos[0], lpos[1], gpos[0], gpos[1], gpos[2], N, rl->flags, rls->occlusion);
+ last_occlusion = rls->occlusion;
+ }
+ lanpr_append_render_line_chain_point(rb,
+ rlc,
+ rl->r->fbcoord[0],
+ rl->r->fbcoord[1],
+ rl->r->gloc[0],
+ rl->r->gloc[1],
+ rl->r->gloc[2],
+ N,
+ rl->flags,
+ last_occlusion);
+
+ /* step 3: grow right */
+ ba = ED_lanpr_get_point_bounding_area_deep(rb, rl->r->fbcoord[0], rl->r->fbcoord[1]);
+ new_rv = rl->r;
+ /* below already done in step 2 */
+ /* lanpr_push_render_line_chain_point(rb,rlc,new_rv->fbcoord[0],new_rv->fbcoord[1],rl->flags,0);
+ */
+ while (ba && (new_rl = lanpr_get_connected_render_line(ba, new_rv, &new_rv, rl->flags))) {
+ new_rl->flags |= LANPR_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 & LANPR_EDGE_FLAG_ALL_TYPE;
+
+ if (new_rv == new_rl->l) {
+ rls = new_rl->segments.last;
+ last_occlusion = rls->occlusion;
+ rlci->occlusion = last_occlusion; /* fix leading vertex occlusion */
+ for (rls = new_rl->segments.last; rls; rls = rls->prev) {
+ double gpos[3], lpos[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, rls->at);
+ last_occlusion = rls->prev ? rls->prev->occlusion : last_occlusion;
+ lanpr_append_render_line_chain_point(rb,
+ rlc,
+ lpos[0],
+ lpos[1],
+ gpos[0],
+ gpos[1],
+ gpos[2],
+ N,
+ new_rl->flags,
+ last_occlusion);
+ }
+ }
+ else if (new_rv == new_rl->r) {
+ rls = new_rl->segments.first;
+ last_occlusion = rls->occlusion;
+ rlci->occlusion = last_occlusion;
+ rls = rls->next;
+ for (; rls; rls = rls->next) {
+ double gpos[3], lpos[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, rls->at);
+ lanpr_append_render_line_chain_point(rb,
+ rlc,
+ lpos[0],
+ lpos[1],
+ gpos[0],
+ gpos[1],
+ gpos[2],
+ N,
+ new_rl->flags,
+ rls->occlusion);
+ last_occlusion = rls->occlusion;
+ }
+ lanpr_append_render_line_chain_point(rb,
+ rlc,
+ new_rl->r->fbcoord[0],
+ new_rl->r->fbcoord[1],
+ new_rl->r->gloc[0],
+ new_rl->r->gloc[1],
+ new_rl->r->gloc[2],
+ N,
+ new_rl->flags,
+ last_occlusion);
+ }
+ ba = ED_lanpr_get_point_bounding_area_deep(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ }
+ }
+}
+
+static LANPR_BoundingArea *lanpr_get_rlci_bounding_area_recursive(LANPR_RenderBuffer *rb,
+ LANPR_BoundingArea *root,
+ LANPR_RenderLineChainItem *rlci)
+{
+ if (root->child == NULL) {
+ return root;
+ }
+ else {
+ LANPR_BoundingArea *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 lanpr_get_rlci_bounding_area_recursive(rb, &ch[0], rlci);
+ }
+ else if (IN_BOUND(ch[1], rlci)) {
+ return lanpr_get_rlci_bounding_area_recursive(rb, &ch[1], rlci);
+ }
+ else if (IN_BOUND(ch[2], rlci)) {
+ return lanpr_get_rlci_bounding_area_recursive(rb, &ch[2], rlci);
+ }
+ else if (IN_BOUND(ch[3], rlci)) {
+ return lanpr_get_rlci_bounding_area_recursive(rb, &ch[3], rlci);
+ }
+#undef IN_BOUND
+ }
+ return NULL;
+}
+static LANPR_BoundingArea *lanpr_get_end_point_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_RenderLineChainItem *rlci)
+{
+ LANPR_BoundingArea *root = ED_lanpr_get_point_bounding_area(rb, rlci->pos[0], rlci->pos[1]);
+ if (root == NULL) {
+ return NULL;
+ }
+ return lanpr_get_rlci_bounding_area_recursive(rb, root, rlci);
+}
+
+/* if reduction threshold is even larger than a small bounding area, */
+/* then 1) geometry is simply too dense. */
+/* 2) probably need to add it to root bounding area which has larger surface area then it
+ * will */
+/* cover typical threshold values. */
+static void lanpr_link_point_with_bounding_area_recursive(LANPR_RenderBuffer *rb,
+ LANPR_BoundingArea *root,
+ LANPR_RenderLineChain *rlc,
+ LANPR_RenderLineChainItem *rlci)
+{
+ if (root->child == NULL) {
+ LANPR_ChainRegisterEntry *cre = list_append_pointer_static_sized(
+ &root->linked_chains, &rb->render_data_pool, rlc, sizeof(LANPR_ChainRegisterEntry));
+
+ cre->rlci = rlci;
+
+ if (rlci == rlc->chain.first) {
+ cre->is_left = 1;
+ }
+ }
+ else {
+ LANPR_BoundingArea *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)) {
+ lanpr_link_point_with_bounding_area_recursive(rb, &ch[0], rlc, rlci);
+ }
+ else if (IN_BOUND(ch[1], rlci)) {
+ lanpr_link_point_with_bounding_area_recursive(rb, &ch[1], rlc, rlci);
+ }
+ else if (IN_BOUND(ch[2], rlci)) {
+ lanpr_link_point_with_bounding_area_recursive(rb, &ch[2], rlc, rlci);
+ }
+ else if (IN_BOUND(ch[3], rlci)) {
+ lanpr_link_point_with_bounding_area_recursive(rb, &ch[3], rlc, rlci);
+ }
+
+#undef IN_BOUND
+ }
+}
+
+static void lanpr_link_chain_with_bounding_areas(LANPR_RenderBuffer *rb,
+ LANPR_RenderLineChain *rlc)
+{
+ LANPR_RenderLineChainItem *pl = rlc->chain.first;
+ LANPR_RenderLineChainItem *pr = rlc->chain.last;
+ LANPR_BoundingArea *ba1 = ED_lanpr_get_point_bounding_area(rb, pl->pos[0], pl->pos[1]);
+ LANPR_BoundingArea *ba2 = ED_lanpr_get_point_bounding_area(rb, pr->pos[0], pr->pos[1]);
+
+ if (ba1) {
+ lanpr_link_point_with_bounding_area_recursive(rb, ba1, rlc, pl);
+ }
+ if (ba2) {
+ lanpr_link_point_with_bounding_area_recursive(rb, ba2, rlc, pr);
+ }
+}
+
+void ED_lanpr_split_chains_for_fixed_occlusion(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderLineChain *rlc, *new_rlc;
+ LANPR_RenderLineChainItem *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);
+ LANPR_RenderLineChainItem *first_rlci = (LANPR_RenderLineChainItem *)rlc->chain.first;
+ int fixed_occ = first_rlci->occlusion;
+ rlc->level = fixed_occ;
+ for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
+ next_rlci = rlci->next;
+ if (rlci->occlusion != fixed_occ) {
+ new_rlc = lanpr_create_render_line_chain(rb);
+ new_rlc->chain.first = rlci;
+ new_rlc->chain.last = rlc->chain.last;
+ rlc->chain.last = rlci->prev;
+ ((LANPR_RenderLineChainItem *)rlc->chain.last)->next = 0;
+ rlci->prev = 0;
+
+ /* end the previous one */
+ lanpr_append_render_line_chain_point(rb,
+ rlc,
+ rlci->pos[0],
+ rlci->pos[1],
+ rlci->gpos[0],
+ rlci->gpos[1],
+ rlci->gpos[2],
+ rlci->normal,
+ rlci->line_type,
+ fixed_occ);
+ new_rlc->object_ref = rlc->object_ref;
+ new_rlc->type = rlc->type;
+ rlc = new_rlc;
+ fixed_occ = rlci->occlusion;
+ rlc->level = fixed_occ;
+ }
+ }
+ }
+ for (rlc = rb->chains.first; rlc; rlc = rlc->next) {
+ lanpr_link_chain_with_bounding_areas(rb, rlc);
+ }
+}
+
+/* note: segment type (crease/material/contour...) is ambiguous after this. */
+static void lanpr_connect_two_chains(LANPR_RenderBuffer *UNUSED(rb),
+ LANPR_RenderLineChain *onto,
+ LANPR_RenderLineChain *sub,
+ int reverse_1,
+ int reverse_2)
+{
+ if (!reverse_1) { /* L--R L-R */
+ if (reverse_2) { /* L--R R-L */
+ BLI_listbase_reverse(&sub->chain);
+ }
+ ((LANPR_RenderLineChainItem *)onto->chain.last)->next = sub->chain.first;
+ ((LANPR_RenderLineChainItem *)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);
+ }
+ ((LANPR_RenderLineChainItem *)sub->chain.last)->next = onto->chain.first;
+ ((LANPR_RenderLineChainItem *)onto->chain.first)->prev = sub->chain.last;
+ onto->chain.first = sub->chain.first;
+ }
+}
+
+/* this only does head-tail connection. */
+/* overlapping / tiny isolated segment / loop reduction not implemented here yet. */
+void ED_lanpr_connect_chains(LANPR_RenderBuffer *rb, const int do_geometry_space)
+{
+ LANPR_RenderLineChain *rlc;
+ LANPR_RenderLineChainItem *rlci;
+ LANPR_BoundingArea *ba;
+ LANPR_ChainRegisterEntry *cre, *next_cre, *closest_cre;
+ float dist;
+ int occlusion;
+ 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;
+ BLI_addtail(&rb->chains, rlc);
+ if (rlc->picked) {
+ continue;
+ }
+
+ rlc->picked = 1;
+
+ occlusion = ((LANPR_RenderLineChainItem *)rlc->chain.first)->occlusion;
+
+ rlci = rlc->chain.last;
+ while ((ba = lanpr_get_end_point_bounding_area(rb, rlci)) != NULL) {
+ dist = do_geometry_space ? rb->chaining_geometry_threshold : rb->chaining_image_threshold;
+ closest_cre = NULL;
+ if (ba->linked_chains.first == NULL) {
+ break;
+ }
+ for (cre = ba->linked_chains.first; cre; cre = next_cre) {
+ next_cre = cre->next;
+ if (cre->rlc->object_ref != rlc->object_ref) {
+ continue;
+ }
+ if (cre->rlc == rlc ||
+ ((LANPR_RenderLineChainItem *)cre->rlc->chain.first)->occlusion != occlusion ||
+ (cre->rlc->type != rlc->type)) {
+ continue;
+ }
+ if (cre->rlc->picked) {
+ BLI_remlink(&ba->linked_chains, cre);
+ 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 (closest_cre) {
+ closest_cre->picked = 1;
+ closest_cre->rlc->picked = 1;
+ BLI_remlink(&ba->linked_chains, cre);
+ if (closest_cre->is_left) {
+ lanpr_connect_two_chains(rb, rlc, closest_cre->rlc, 0, 0);
+ }
+ else {
+ lanpr_connect_two_chains(rb, rlc, closest_cre->rlc, 0, 1);
+ }
+ BLI_remlink(&swap, closest_cre->rlc);
+ }
+ else {
+ break;
+ }
+ rlci = rlc->chain.last;
+ }
+
+ rlci = rlc->chain.first;
+ while ((ba = lanpr_get_end_point_bounding_area(rb, rlci)) != NULL) {
+ dist = do_geometry_space ? rb->chaining_geometry_threshold : rb->chaining_image_threshold;
+ closest_cre = NULL;
+ if (ba->linked_chains.first == NULL) {
+ break;
+ }
+ for (cre = ba->linked_chains.first; cre; cre = next_cre) {
+ next_cre = cre->next;
+ if (cre->rlc->object_ref != rlc->object_ref) {
+ continue;
+ }
+ if (cre->rlc == rlc ||
+ ((LANPR_RenderLineChainItem *)cre->rlc->chain.first)->occlusion != occlusion ||
+ (cre->rlc->type != rlc->type)) {
+ continue;
+ }
+ if (cre->rlc->picked) {
+ BLI_remlink(&ba->linked_chains, cre);
+ 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 (closest_cre) {
+ closest_cre->picked = 1;
+ closest_cre->rlc->picked = 1;
+ BLI_remlink(&ba->linked_chains, cre);
+ if (closest_cre->is_left) {
+ lanpr_connect_two_chains(rb, rlc, closest_cre->rlc, 1, 0);
+ }
+ else {
+ lanpr_connect_two_chains(rb, rlc, closest_cre->rlc, 1, 1);
+ }
+ BLI_remlink(&swap, closest_cre->rlc);
+ }
+ else {
+ break;
+ }
+ rlci = rlc->chain.first;
+ }
+ }
+}
+
+/* length is in image space */
+float ED_lanpr_compute_chain_length(LANPR_RenderLineChain *rlc)
+{
+ LANPR_RenderLineChainItem *rlci;
+ float offset_accum = 0;
+ float dist;
+ float last_point[2];
+
+ rlci = rlc->chain.first;
+ copy_v2_v2(last_point, rlci->pos);
+ for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
+ dist = len_v2v2(rlci->pos, last_point);
+ offset_accum += dist;
+ copy_v2_v2(last_point, rlci->pos);
+ }
+ return offset_accum;
+}
+
+void ED_lanpr_discard_short_chains(LANPR_RenderBuffer *rb, const float threshold)
+{
+ LANPR_RenderLineChain *rlc, *next_rlc;
+ for (rlc = rb->chains.first; rlc; rlc = next_rlc) {
+ next_rlc = rlc->next;
+ if (ED_lanpr_compute_chain_length(rlc) < threshold) {
+ BLI_remlink(&rb->chains, rlc);
+ }
+ }
+}
+
+int ED_lanpr_count_chain(const LANPR_RenderLineChain *rlc)
+{
+ LANPR_RenderLineChainItem *rlci;
+ int count = 0;
+ for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
+ count++;
+ }
+ return count;
+}
+
+void ED_lanpr_chain_clear_picked_flag(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderLineChain *rlc;
+ if (rb == NULL) {
+ return;
+ }
+ for (rlc = rb->chains.first; rlc; rlc = rlc->next) {
+ rlc->picked = 0;
+ }
+}
diff --git a/source/blender/editors/lanpr/lanpr_cpu.c b/source/blender/editors/lanpr/lanpr_cpu.c
new file mode 100644
index 00000000000..38f82b53418
--- /dev/null
+++ b/source/blender/editors/lanpr/lanpr_cpu.c
@@ -0,0 +1,4467 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#include "ED_lanpr.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_camera.h"
+#include "BKE_collection.h"
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_gpencil.h"
+#include "BKE_object.h"
+#include "BKE_report.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_gpencil_types.h"
+#include "DNA_lanpr_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 "BKE_text.h"
+
+#include "lanpr_intern.h"
+
+extern LANPR_SharedResource lanpr_share;
+extern const char *RE_engine_id_BLENDER_LANPR;
+
+/* Own functions */
+
+static LANPR_BoundingArea *lanpr_get_first_possible_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_RenderLine *rl);
+
+static void lanpr_link_line_with_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_BoundingArea *root_ba,
+ LANPR_RenderLine *rl);
+
+static LANPR_BoundingArea *lanpr_get_next_bounding_area(LANPR_BoundingArea *This,
+ LANPR_RenderLine *rl,
+ double x,
+ double y,
+ double k,
+ int positive_x,
+ int positive_y,
+ double *next_x,
+ double *next_y);
+static int lanpr_triangle_line_imagespace_intersection_v2(SpinLock *spl,
+ const LANPR_RenderTriangle *rt,
+ const LANPR_RenderLine *rl,
+ const double *override_camera_loc,
+ const char override_cam_is_persp,
+ const double vp[4][4],
+ const double *camera_dir,
+ const double cam_shift_x,
+ const double cam_shift_y,
+ double *from,
+ double *to);
+static int lanpr_get_line_bounding_areas(LANPR_RenderBuffer *rb,
+ LANPR_RenderLine *rl,
+ int *rowbegin,
+ int *rowend,
+ int *colbegin,
+ int *colend);
+
+/* Layer operations */
+
+static void lanpr_line_layer_unique_name(ListBase *list, LANPR_LineLayer *ll, const char *defname)
+{
+ BLI_uniquename(list, ll, defname, '.', offsetof(LANPR_LineLayer, name), sizeof(ll->name));
+}
+
+int ED_lanpr_max_occlusion_in_line_layers(SceneLANPR *lanpr)
+{
+ LANPR_LineLayer *lli;
+ int max_occ = -1, max;
+ for (lli = lanpr->line_layers.first; lli; lli = lli->next) {
+ if (lli->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS) {
+ max = MAX2(lli->level_start, lli->level_end);
+ }
+ else {
+ max = lli->level_start;
+ }
+ max_occ = MAX2(max, max_occ);
+ }
+ return max_occ;
+}
+LANPR_LineLayer *ED_lanpr_new_line_layer(SceneLANPR *lanpr)
+{
+ LANPR_LineLayer *ll = MEM_callocN(sizeof(LANPR_LineLayer), "Line Layer");
+
+ lanpr_line_layer_unique_name(&lanpr->line_layers, ll, "Layer");
+
+ int max_occ = ED_lanpr_max_occlusion_in_line_layers(lanpr);
+
+ ll->level_start = ll->level_end = max_occ + 1;
+ ll->flags |= LANPR_LINE_LAYER_USE_SAME_STYLE;
+ ll->thickness = 1.0f;
+ copy_v3_fl(ll->color, 0.8);
+ ll->color[3] = 1.0f;
+ ll->contour.use = 1;
+ ll->crease.use = 1;
+ ll->material_separate.use = 1;
+ ll->edge_mark.use = 1;
+ ll->intersection.use = 1;
+
+ ll->normal_thickness_start = 0.2f;
+ ll->normal_thickness_end = 1.5f;
+ ll->normal_ramp_begin = 0.0f;
+ ll->normal_ramp_end = 1.0f;
+
+ ll->normal_mode = LANPR_NORMAL_DIRECTIONAL;
+
+ lanpr->active_layer = ll;
+ BLI_addtail(&lanpr->line_layers, ll);
+
+ return ll;
+}
+static int lanpr_add_line_layer_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneLANPR *lanpr = &scene->lanpr;
+
+ ED_lanpr_new_line_layer(lanpr);
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+static int lanpr_delete_line_layer_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneLANPR *lanpr = &scene->lanpr;
+
+ LANPR_LineLayer *ll = lanpr->active_layer;
+
+ if (ll == NULL) {
+ return OPERATOR_FINISHED;
+ }
+
+ if (ll->prev) {
+ lanpr->active_layer = ll->prev;
+ }
+ else if (ll->next) {
+ lanpr->active_layer = ll->next;
+ }
+ else {
+ lanpr->active_layer = 0;
+ }
+
+ BLI_remlink(&scene->lanpr.line_layers, ll);
+
+ MEM_freeN(ll);
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+static int lanpr_move_line_layer_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneLANPR *lanpr = &scene->lanpr;
+
+ LANPR_LineLayer *ll = lanpr->active_layer;
+
+ if (ll == NULL) {
+ return OPERATOR_FINISHED;
+ }
+
+ int dir = RNA_enum_get(op->ptr, "direction");
+
+ if (dir == 1 && ll->prev) {
+ BLI_remlink(&lanpr->line_layers, ll);
+ BLI_insertlinkbefore(&lanpr->line_layers, ll->prev, ll);
+ }
+ else if (dir == -1 && ll->next) {
+ BLI_remlink(&lanpr->line_layers, ll);
+ BLI_insertlinkafter(&lanpr->line_layers, ll->next, ll);
+ }
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int ED_lanpr_rebuild_all_commands_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+
+ ED_lanpr_rebuild_all_command(scene);
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+static int lanpr_enable_all_line_types_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneLANPR *lanpr = &scene->lanpr;
+ LANPR_LineLayer *ll;
+
+ if (!(ll = lanpr->active_layer)) {
+ return OPERATOR_FINISHED;
+ }
+
+ ll->contour.use = 1;
+ ll->crease.use = 1;
+ ll->edge_mark.use = 1;
+ ll->material_separate.use = 1;
+ ll->intersection.use = 1;
+
+ copy_v3_v3(ll->contour.color, ll->color);
+ copy_v3_v3(ll->crease.color, ll->color);
+ copy_v3_v3(ll->edge_mark.color, ll->color);
+ copy_v3_v3(ll->material_separate.color, ll->color);
+ copy_v3_v3(ll->intersection.color, ll->color);
+
+ ll->contour.thickness = 1;
+ ll->crease.thickness = 1;
+ ll->material_separate.thickness = 1;
+ ll->edge_mark.thickness = 1;
+ ll->intersection.thickness = 1;
+
+ return OPERATOR_FINISHED;
+}
+static int lanpr_auto_create_line_layer_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneLANPR *lanpr = &scene->lanpr;
+
+ LANPR_LineLayer *ll;
+
+ ll = ED_lanpr_new_line_layer(lanpr);
+ ll->thickness = 1.7;
+
+ lanpr_enable_all_line_types_exec(C, op);
+
+ ll = ED_lanpr_new_line_layer(lanpr);
+ ll->thickness = 0.9;
+ copy_v3_fl(ll->color, 0.6);
+
+ lanpr_enable_all_line_types_exec(C, op);
+
+ ll = ED_lanpr_new_line_layer(lanpr);
+ ll->thickness = 0.7;
+ copy_v3_fl(ll->color, 0.5);
+
+ lanpr_enable_all_line_types_exec(C, op);
+
+ ED_lanpr_rebuild_all_command(scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void SCENE_OT_lanpr_add_line_layer(wmOperatorType *ot)
+{
+
+ ot->name = "Add Line Layer";
+ ot->description = "Add a new line layer";
+ ot->idname = "SCENE_OT_lanpr_add_line_layer";
+
+ ot->exec = lanpr_add_line_layer_exec;
+}
+void SCENE_OT_lanpr_delete_line_layer(wmOperatorType *ot)
+{
+
+ ot->name = "Delete Line Layer";
+ ot->description = "Delete selected line layer";
+ ot->idname = "SCENE_OT_lanpr_delete_line_layer";
+
+ ot->exec = lanpr_delete_line_layer_exec;
+}
+void SCENE_OT_lanpr_rebuild_all_commands(wmOperatorType *ot)
+{
+
+ ot->name = "Refresh Drawing Commands";
+ ot->description = "Refresh LANPR line layer drawing commands";
+ ot->idname = "SCENE_OT_lanpr_rebuild_all_commands";
+
+ ot->exec = ED_lanpr_rebuild_all_commands_exec;
+}
+void SCENE_OT_lanpr_auto_create_line_layer(wmOperatorType *ot)
+{
+
+ ot->name = "Auto Create Line Layer";
+ ot->description = "Automatically create defalt line layer config";
+ ot->idname = "SCENE_OT_lanpr_auto_create_line_layer";
+
+ ot->exec = lanpr_auto_create_line_layer_exec;
+}
+void SCENE_OT_lanpr_move_line_layer(wmOperatorType *ot)
+{
+ static const EnumPropertyItem line_layer_move[] = {
+ {1, "UP", 0, "Up", ""}, {-1, "DOWN", 0, "Down", ""}, {0, NULL, 0, NULL, NULL}};
+
+ ot->name = "Move Line Layer";
+ ot->description = "Move LANPR line layer up and down";
+ ot->idname = "SCENE_OT_lanpr_move_line_layer";
+
+ /* this need property to assign up/down direction */
+
+ ot->exec = lanpr_move_line_layer_exec;
+
+ RNA_def_enum(ot->srna,
+ "direction",
+ line_layer_move,
+ 0,
+ "Direction",
+ "Direction to move the active line layer towards");
+}
+void SCENE_OT_lanpr_enable_all_line_types(wmOperatorType *ot)
+{
+ ot->name = "Enable All Line Types";
+ ot->description = "Enable All Line Types In This Line Layer";
+ ot->idname = "SCENE_OT_lanpr_enable_all_line_types";
+
+ ot->exec = lanpr_enable_all_line_types_exec;
+}
+
+/* Geometry */
+
+int use_smooth_contour_modifier_contour = 0; /* debug purpose */
+
+static void lanpr_cut_render_line(LANPR_RenderBuffer *rb,
+ LANPR_RenderLine *rl,
+ double begin,
+ double end)
+{
+ LANPR_RenderLineSegment *rls = rl->segments.first, *irls;
+ LANPR_RenderLineSegment *begin_segment = 0, *end_segment = 0;
+ LANPR_RenderLineSegment *ns = 0, *ns2 = 0;
+ int untouched = 0;
+
+ if (TNS_DOUBLE_CLOSE_ENOUGH(begin, end)) {
+ return;
+ }
+
+ if (begin != begin) {
+ begin = 0;
+ }
+ if (end != end) {
+ end = 0;
+ }
+
+ if (begin > end) {
+ double t = begin;
+ begin = end;
+ end = t;
+ }
+
+ for (rls = rl->segments.first; rls; rls = rls->next) {
+ if (TNS_DOUBLE_CLOSE_ENOUGH(rls->at, begin)) {
+ begin_segment = rls;
+ ns = begin_segment;
+ break;
+ }
+ if (rls->next == NULL) {
+ break;
+ }
+ irls = rls->next;
+ if (irls->at > begin + 1e-09 && begin > rls->at) {
+ begin_segment = irls;
+ ns = mem_static_aquire_thread(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ break;
+ }
+ }
+ if (!begin_segment && TNS_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ untouched = 1;
+ }
+ for (rls = begin_segment; rls; rls = rls->next) {
+ if (TNS_DOUBLE_CLOSE_ENOUGH(rls->at, end)) {
+ end_segment = rls;
+ ns2 = end_segment;
+ break;
+ }
+ /* irls = rls->next; */
+ /* added this to prevent rls->at == 1.0 (we don't need an end point for this) */
+ if (!rls->next && TNS_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ end_segment = rls;
+ ns2 = end_segment;
+ untouched = 1;
+ break;
+ }
+ else if (rls->at > end) {
+ end_segment = rls;
+ ns2 = mem_static_aquire_thread(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ break;
+ }
+ }
+
+ if (ns == NULL) {
+ ns = mem_static_aquire_thread(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ }
+ if (ns2 == NULL) {
+ if (untouched) {
+ ns2 = ns;
+ end_segment = ns2;
+ }
+ else
+ ns2 = mem_static_aquire_thread(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ }
+
+ if (begin_segment) {
+ if (begin_segment != ns) {
+ ns->occlusion = begin_segment->prev ? (irls = begin_segment->prev)->occlusion : 0;
+ BLI_insertlinkbefore(&rl->segments, (void *)begin_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 = begin;
+ if (!untouched) {
+ ns2->at = end;
+ }
+ else {
+ ns2 = ns2->next;
+ }
+
+ for (rls = ns; rls && rls != ns2; rls = rls->next) {
+ rls->occlusion++;
+ }
+
+ char min_occ = 127;
+ for (rls = rl->segments.first; rls; rls = rls->next) {
+ min_occ = MIN2(min_occ, rls->occlusion);
+ }
+ rl->min_occ = min_occ;
+}
+static int lanpr_make_next_occlusion_task_info(LANPR_RenderBuffer *rb, LANPR_RenderTaskInfo *rti)
+{
+ LinkData *data;
+ int i;
+ int res = 0;
+
+ BLI_spin_lock(&rb->lock_task);
+
+ if (rb->contour_managed) {
+ data = rb->contour_managed;
+ rti->contour = (void *)data;
+ rti->contour_pointers.first = data;
+ for (i = 0; i < TNS_THREAD_LINE_COUNT && data; i++) {
+ data = data->next;
+ }
+ rb->contour_managed = data;
+ rti->contour_pointers.last = data ? data->prev : rb->contours.last;
+ res = 1;
+ }
+ else {
+ BLI_listbase_clear(&rti->contour_pointers);
+ rti->contour = 0;
+ }
+
+ if (rb->intersection_managed) {
+ data = rb->intersection_managed;
+ rti->intersection = (void *)data;
+ rti->intersection_pointers.first = data;
+ for (i = 0; i < TNS_THREAD_LINE_COUNT && data; i++) {
+ data = data->next;
+ }
+ rb->intersection_managed = data;
+ rti->intersection_pointers.last = data ? data->prev : rb->intersection_lines.last;
+ res = 1;
+ }
+ else {
+ BLI_listbase_clear(&rti->intersection_pointers);
+ rti->intersection = 0;
+ }
+
+ if (rb->crease_managed) {
+ data = rb->crease_managed;
+ rti->crease = (void *)data;
+ rti->crease_pointers.first = data;
+ for (i = 0; i < TNS_THREAD_LINE_COUNT && data; i++) {
+ data = data->next;
+ }
+ rb->crease_managed = data;
+ rti->crease_pointers.last = data ? data->prev : rb->crease_lines.last;
+ res = 1;
+ }
+ else {
+ BLI_listbase_clear(&rti->crease_pointers);
+ rti->crease = 0;
+ }
+
+ if (rb->material_managed) {
+ data = rb->material_managed;
+ rti->material = (void *)data;
+ rti->material_pointers.first = data;
+ for (i = 0; i < TNS_THREAD_LINE_COUNT && data; i++) {
+ data = data->next;
+ }
+ rb->material_managed = data;
+ rti->material_pointers.last = data ? data->prev : rb->material_lines.last;
+ res = 1;
+ }
+ else {
+ BLI_listbase_clear(&rti->material_pointers);
+ rti->material = 0;
+ }
+
+ if (rb->edge_mark_managed) {
+ data = rb->edge_mark_managed;
+ rti->edge_mark = (void *)data;
+ rti->edge_mark_pointers.first = data;
+ for (i = 0; i < TNS_THREAD_LINE_COUNT && data; i++) {
+ data = data->next;
+ }
+ rb->edge_mark_managed = data;
+ rti->edge_mark_pointers.last = data ? data->prev : rb->edge_marks.last;
+ res = 1;
+ }
+ else {
+ BLI_listbase_clear(&rti->edge_mark_pointers);
+ rti->edge_mark = 0;
+ }
+
+ BLI_spin_unlock(&rb->lock_task);
+
+ return res;
+}
+static void lanpr_calculate_single_line_occlusion(LANPR_RenderBuffer *rb,
+ LANPR_RenderLine *rl,
+ int thread_id)
+{
+ double x = rl->l->fbcoord[0], y = rl->l->fbcoord[1];
+ LANPR_BoundingArea *ba = lanpr_get_first_possible_bounding_area(rb, rl);
+ LANPR_BoundingArea *nba = ba;
+ LANPR_RenderTriangleThread *rt;
+ LinkData *lip;
+ 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) {
+
+ for (lip = nba->linked_triangles.first; lip; lip = lip->next) {
+ rt = lip->data;
+ if (rt->testing[thread_id] == rl || rl->l->intersecting_with == (void *)rt ||
+ rl->r->intersecting_with == (void *)rt) {
+ continue;
+ }
+ rt->testing[thread_id] = rl;
+ if (lanpr_triangle_line_imagespace_intersection_v2(&rb->lock_task,
+ (void *)rt,
+ rl,
+ rb->camera_pos,
+ rb->cam_is_persp,
+ rb->view_projection,
+ rb->view_vector,
+ rb->shift_x,
+ rb->shift_y,
+ &l,
+ &r)) {
+ lanpr_cut_render_line(rb, rl, l, r);
+ if (rl->min_occ > rb->max_occlusion_level) {
+ return; /* No need to caluclate any longer. */
+ }
+ }
+ }
+
+ nba = lanpr_get_next_bounding_area(nba, rl, x, y, k, positive_x, positive_y, &x, &y);
+ }
+}
+
+static bool lanpr_calculation_is_canceled(void)
+{
+ bool is_canceled;
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+ switch (lanpr_share.flag_render_status) {
+ case LANPR_RENDER_INCOMPELTE:
+ is_canceled = true;
+ break;
+ default:
+ is_canceled = false;
+ }
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+ return is_canceled;
+}
+static void lanpr_calculate_line_occlusion_worker(TaskPool *__restrict UNUSED(pool),
+ LANPR_RenderTaskInfo *rti)
+{
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+ LinkData *lip;
+
+ while (lanpr_make_next_occlusion_task_info(rb, rti)) {
+
+ for (lip = (void *)rti->contour; lip && lip->prev != rti->contour_pointers.last;
+ lip = lip->next) {
+ lanpr_calculate_single_line_occlusion(rb, lip->data, rti->thread_id);
+ }
+
+ /* Monitoring cancelation flag every once a while. */
+ if (lanpr_calculation_is_canceled())
+ return;
+
+ for (lip = (void *)rti->crease; lip && lip->prev != rti->crease_pointers.last;
+ lip = lip->next) {
+ lanpr_calculate_single_line_occlusion(rb, lip->data, rti->thread_id);
+ }
+
+ if (lanpr_calculation_is_canceled())
+ return;
+
+ for (lip = (void *)rti->intersection; lip && lip->prev != rti->intersection_pointers.last;
+ lip = lip->next) {
+ lanpr_calculate_single_line_occlusion(rb, lip->data, rti->thread_id);
+ }
+
+ if (lanpr_calculation_is_canceled())
+ return;
+
+ for (lip = (void *)rti->material; lip && lip->prev != rti->material_pointers.last;
+ lip = lip->next) {
+ lanpr_calculate_single_line_occlusion(rb, lip->data, rti->thread_id);
+ }
+
+ if (lanpr_calculation_is_canceled())
+ return;
+
+ for (lip = (void *)rti->edge_mark; lip && lip->prev != rti->edge_mark_pointers.last;
+ lip = lip->next) {
+ lanpr_calculate_single_line_occlusion(rb, lip->data, rti->thread_id);
+ }
+
+ if (lanpr_calculation_is_canceled())
+ return;
+ }
+}
+static void lanpr_calculate_line_occlusion_begin(LANPR_RenderBuffer *rb)
+{
+ int thread_count = rb->thread_count;
+ LANPR_RenderTaskInfo *rti = MEM_callocN(sizeof(LANPR_RenderTaskInfo) * thread_count,
+ "Task Pool");
+ int i;
+
+ rb->contour_managed = rb->contours.first;
+ rb->crease_managed = rb->crease_lines.first;
+ rb->intersection_managed = rb->intersection_lines.first;
+ rb->material_managed = rb->material_lines.first;
+ rb->edge_mark_managed = rb->edge_marks.first;
+
+ TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH);
+
+ for (i = 0; i < thread_count; i++) {
+ rti[i].thread_id = i;
+ BLI_task_pool_push(
+ tp, (TaskRunFunction)lanpr_calculate_line_occlusion_worker, &rti[i], 0, NULL);
+ }
+ BLI_task_pool_work_and_wait(tp);
+ BLI_task_pool_free(tp);
+
+ MEM_freeN(rti);
+}
+
+int ED_lanpr_point_inside_triangled(double v[2], double v0[2], double v1[2], double v2[2])
+{
+ double cl, c;
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ c = cl;
+
+ cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]);
+ if (c * cl <= 0) {
+ return 0;
+ }
+ else
+ c = cl;
+
+ cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]);
+ if (c * cl <= 0) {
+ return 0;
+ }
+ else
+ c = cl;
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ if (c * cl <= 0) {
+ return 0;
+ }
+
+ return 1;
+}
+static int lanpr_point_on_lined(double v[2], double v0[2], double v1[2])
+{
+ double c1, c2;
+
+ c1 = tmat_get_linear_ratio(v0[0], v1[0], v[0]);
+ c2 = tmat_get_linear_ratio(v0[1], v1[1], v[1]);
+
+ if (TNS_DOUBLE_CLOSE_ENOUGH(c1, c2) && c1 >= 0 && c1 <= 1) {
+ return 1;
+ }
+
+ return 0;
+}
+static int lanpr_point_triangle_relation(double v[2], double v0[2], double v1[2], double v2[2])
+{
+ double cl, c;
+ double r;
+ if (lanpr_point_on_lined(v, v0, v1) || lanpr_point_on_lined(v, v1, v2) ||
+ lanpr_point_on_lined(v, v2, v0)) {
+ return 1;
+ }
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ c = cl;
+
+ cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]);
+ if ((r = c * cl) < 0) {
+ return 0;
+ }
+ /* else if(r == 0) return 1; // removed, point could still be on the extention line of some edge
+ */
+ else
+ c = cl;
+
+ cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]);
+ if ((r = c * cl) < 0) {
+ return 0;
+ }
+ /* else if(r == 0) return 1; */
+ else
+ c = cl;
+
+ cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
+ if ((r = c * cl) < 0) {
+ return 0;
+ }
+ else if (r == 0) {
+ return 1;
+ }
+
+ return 2;
+}
+static int lanpr_point_inside_triangle3de(double v[3], double v0[3], double v1[3], double v2[3])
+{
+ double l[3], r[3];
+ double N1[3], N2[3];
+ double d;
+
+ sub_v3_v3v3_db(l, v1, v0);
+ sub_v3_v3v3_db(r, v, v1);
+ cross_v3_v3v3_db(N1, l, r);
+
+ sub_v3_v3v3_db(l, v2, v1);
+ sub_v3_v3v3_db(r, v, v2);
+ cross_v3_v3v3_db(N2, l, r);
+
+ if ((d = dot_v3v3_db(N1, N2)) < 0) {
+ return 0;
+ }
+
+ sub_v3_v3v3_db(l, v0, v2);
+ sub_v3_v3v3_db(r, v, v0);
+ cross_v3_v3v3_db(N1, l, r);
+
+ if ((d = dot_v3v3_db(N1, N2)) < 0) {
+ return 0;
+ }
+
+ sub_v3_v3v3_db(l, v1, v0);
+ sub_v3_v3v3_db(r, v, v1);
+ cross_v3_v3v3_db(N2, l, r);
+
+ if ((d = dot_v3v3_db(N1, N2)) < 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static LANPR_RenderElementLinkNode *lanpr_new_cull_triangle_space64(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderElementLinkNode *reln;
+
+ LANPR_RenderTriangle *render_triangles = mem_static_aquire(
+ &rb->render_data_pool,
+ 64 * rb->triangle_size); /* CreateNewBuffer(LANPR_RenderTriangle, 64); */
+
+ reln = list_append_pointer_static_sized(&rb->triangle_buffer_pointers,
+ &rb->render_data_pool,
+ render_triangles,
+ sizeof(LANPR_RenderElementLinkNode));
+ reln->element_count = 64;
+ reln->additional = 1;
+
+ return reln;
+}
+static LANPR_RenderElementLinkNode *lanpr_new_cull_point_space64(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderElementLinkNode *reln;
+
+ LANPR_RenderVert *render_vertices = mem_static_aquire(&rb->render_data_pool,
+ sizeof(LANPR_RenderVert) * 64);
+
+ reln = list_append_pointer_static_sized(&rb->vertex_buffer_pointers,
+ &rb->render_data_pool,
+ render_vertices,
+ sizeof(LANPR_RenderElementLinkNode));
+ reln->element_count = 64;
+ reln->additional = 1;
+
+ return reln;
+}
+static void lanpr_assign_render_line_with_triangle(LANPR_RenderTriangle *rt)
+{
+ if (rt->rl[0]->tl == NULL) {
+ rt->rl[0]->tl = rt;
+ }
+ else if (rt->rl[0]->tr == NULL) {
+ rt->rl[0]->tr = rt;
+ }
+
+ if (rt->rl[1]->tl == NULL) {
+ rt->rl[1]->tl = rt;
+ }
+ else if (rt->rl[1]->tr == NULL) {
+ rt->rl[1]->tr = rt;
+ }
+
+ if (rt->rl[2]->tl == NULL) {
+ rt->rl[2]->tl = rt;
+ }
+ else if (rt->rl[2]->tr == NULL) {
+ rt->rl[2]->tr = rt;
+ }
+}
+static void lanpr_post_triangle(LANPR_RenderTriangle *rt, LANPR_RenderTriangle *orig)
+{
+ if (rt->v[0]) {
+ add_v3_v3_db(rt->gc, rt->v[0]->fbcoord);
+ }
+ if (rt->v[1]) {
+ add_v3_v3_db(rt->gc, rt->v[1]->fbcoord);
+ }
+ if (rt->v[2]) {
+ add_v3_v3_db(rt->gc, rt->v[2]->fbcoord);
+ }
+ mul_v3db_db(rt->gc, 1.0f / 3.0f);
+
+ copy_v3_v3_db(rt->gn, orig->gn);
+}
+
+#define RT_AT(head, rb, offset) ((unsigned char *)head + offset * rb->triangle_size)
+
+/** This function cuts triangles that are (partially or fully) behind near clipping plane.
+ * for triangles that crossing the near plane, it will generate new 1 or 2 triangles with
+ * new topology that represents the trimmed triangle. (which then became a triangle or square)
+ */
+static void lanpr_cull_triangles(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderLine *rl;
+ LANPR_RenderTriangle *rt, *rt1, *rt2;
+ LANPR_RenderVert *rv;
+ LANPR_RenderElementLinkNode *reln, *veln, *teln;
+ LANPR_RenderLineSegment *rls;
+ double(*vp)[4] = rb->view_projection;
+ int i;
+ double a;
+ int v_count = 0, t_count = 0;
+ Object *o;
+
+ 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);
+
+ double cam_pos[3];
+ double clip_start;
+ copy_v3_v3_db(cam_pos, rb->camera_pos);
+ clip_start = rb->near_clip;
+ mul_v3db_db(clip_advance, -clip_start);
+ add_v3_v3_db(cam_pos, clip_advance);
+
+ veln = lanpr_new_cull_point_space64(rb);
+ teln = lanpr_new_cull_triangle_space64(rb);
+ rv = &((LANPR_RenderVert *)veln->pointer)[v_count];
+ rt1 = (void *)(((unsigned char *)teln->pointer) + rb->triangle_size * t_count);
+
+ for (reln = rb->triangle_buffer_pointers.first; reln; reln = reln->next) {
+ if (reln->additional) {
+ continue;
+ }
+ o = reln->object_ref;
+ for (i = 0; i < reln->element_count; i++) {
+
+ /* These three represents points that are in the clipping range or not*/
+ int in0 = 0, in1 = 0, in2 = 0;
+
+ /* Select the triangle in the array. */
+ rt = (void *)(((unsigned char *)reln->pointer) + rb->triangle_size * i);
+
+ /* Point inside near plane */
+ if (rt->v[0]->fbcoord[2] < 0) {
+ in0 = 1;
+ }
+ if (rt->v[1]->fbcoord[2] < 0) {
+ in1 = 1;
+ }
+ if (rt->v[2]->fbcoord[2] < 0) {
+ in2 = 1;
+ }
+
+ /* Additional memory space for storing generated points and triangles */
+ if (v_count > 60) {
+ veln->element_count = v_count;
+ veln = lanpr_new_cull_point_space64(rb);
+ v_count = 0;
+ }
+ if (t_count > 60) {
+ teln->element_count = t_count;
+ teln = lanpr_new_cull_triangle_space64(rb);
+ t_count = 0;
+ }
+
+ rv = &((LANPR_RenderVert *)veln->pointer)[v_count];
+ rt1 = (void *)(((unsigned char *)teln->pointer) + rb->triangle_size * t_count);
+ rt2 = (void *)(((unsigned char *)teln->pointer) + rb->triangle_size * (t_count + 1));
+
+ double vv1[3], vv2[3], dot1, dot2;
+
+ switch (in0 + in1 + in2) {
+ case 0: /* ignore this triangle. */
+ continue;
+ case 3:
+ /** triangle completely behind near plane, throw it away
+ * also remove render lines form being computed.
+ */
+ rt->cull_status = LANPR_CULL_DISCARD;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[0]);
+ rt->rl[0]->next = rt->rl[0]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[1]);
+ rt->rl[1]->next = rt->rl[1]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[2]);
+ rt->rl[2]->next = rt->rl[2]->prev = 0;
+ continue;
+ case 2:
+ /** Two points behind near plane, cut those and
+ * generate 2 new points, 3 lines and 1 triangle */
+ rt->cull_status = LANPR_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);
+
+ /* 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);
+
+ /* remove all original render lines */
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[0]);
+ rt->rl[0]->next = rt->rl[0]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[1]);
+ rt->rl[1]->next = rt->rl[1]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[2]);
+ rt->rl[2]->next = rt->rl[2]->prev = 0;
+
+ /* New line connecting two new points */
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, 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;
+ rt1->rl[1] = rl;
+ rl->object_ref = o;
+
+ /* new line connecting original point 0 and a new point */
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[1];
+ rl->r = rt->v[0];
+ /* restore adjacent triangle data. */
+ rl->tl = rt->rl[0]->tl == rt ? rt1 : rt->rl[0]->tl;
+ rl->tr = rt->rl[0]->tr == rt ? rt1 : rt->rl[0]->tr;
+ rt1->rl[0] = rl;
+ rl->object_ref = o;
+
+ /* new line connecting original point 0 and another new point */
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[0];
+ rl->r = &rv[0];
+ /* restore adjacent triangle data. */
+ rl->tl = rt->rl[2]->tl == rt ? rt1 : rt->rl[2]->tl;
+ rl->tr = rt->rl[2]->tr == rt ? rt1 : rt->rl[2]->tr;
+ rt1->rl[2] = rl;
+ rl->object_ref = o;
+
+ /* 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];
+
+ lanpr_post_triangle(rt1, rt);
+
+ v_count += 2;
+ t_count += 1;
+ continue;
+ }
+ 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);
+
+ 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);
+
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[0]);
+ rt->rl[0]->next = rt->rl[0]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[1]);
+ rt->rl[1]->next = rt->rl[1]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[2]);
+ rt->rl[2]->next = rt->rl[2]->prev = 0;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[0];
+ rl->r = &rv[1];
+ rl->tl = rt1;
+ rt1->rl[0] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[1];
+ rl->r = rt->v[2];
+ rl->tl = rt->rl[1]->tl == rt ? rt1 : rt->rl[1]->tl;
+ rl->tr = rt->rl[1]->tr == rt ? rt1 : rt->rl[1]->tr;
+ rt1->rl[1] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[2];
+ rl->r = &rv[0];
+ rl->tl = rt->rl[2]->tl == rt ? rt1 : rt->rl[2]->tl;
+ rl->tr = rt->rl[2]->tr == rt ? rt1 : rt->rl[2]->tr;
+ rt1->rl[2] = rl;
+ rl->object_ref = o;
+
+ rt1->v[0] = &rv[0]; /*&rv[1];*/
+ rt1->v[1] = &rv[1]; /*rt->v[2];*/
+ rt1->v[2] = rt->v[2]; /*&rv[0];*/
+
+ lanpr_post_triangle(rt1, rt);
+
+ v_count += 2;
+ t_count += 1;
+ continue;
+ }
+ 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);
+
+ 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);
+
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[0]);
+ rt->rl[0]->next = rt->rl[0]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[1]);
+ rt->rl[1]->next = rt->rl[1]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[2]);
+ rt->rl[2]->next = rt->rl[2]->prev = 0;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rt1->rl[2] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[0];
+ rl->r = rt->v[1];
+ rl->tl = rt->rl[1]->tl == rt ? rt1 : rt->rl[1]->tl;
+ rl->tr = rt->rl[1]->tr == rt ? rt1 : rt->rl[1]->tr;
+ rt1->rl[0] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[1];
+ rl->r = &rv[1];
+ rl->tl = rt->rl[0]->tl == rt ? rt1 : rt->rl[0]->tl;
+ rl->tr = rt->rl[0]->tr == rt ? rt1 : rt->rl[0]->tr;
+ rt1->rl[1] = rl;
+ rl->object_ref = o;
+
+ rt1->v[0] = &rv[0]; /*rt->v[1];*/
+ rt1->v[1] = rt->v[1]; /*&rv[1];*/
+ rt1->v[2] = &rv[1]; /*&rv[0];*/
+
+ lanpr_post_triangle(rt1, rt);
+
+ v_count += 2;
+ t_count += 1;
+ continue;
+ }
+ break;
+ case 1:
+ /** Two points behind near plane, cut those and
+ * generate 2 new points, 4 lines and 2 triangles */
+ rt->cull_status = LANPR_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);
+
+ /* 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);
+
+ /* Remove two cutted lines, the visible line is untouched. */
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[0]);
+ rt->rl[0]->next = rt->rl[0]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[2]);
+ rt->rl[2]->next = rt->rl[2]->prev = 0;
+
+ /* New line connects two new points */
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rt1->rl[1] = rl;
+ rl->object_ref = o;
+
+ /** New line connects new point 0 and old point 1,
+ * this is a border line.
+ */
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[0];
+ rl->r = rt->v[1];
+ rl->tl = rt1;
+ rl->tr = rt->rl[0]->tr == rt ? rt->rl[0]->tl : rt->rl[0]->tr;
+ rt1->rl[2] = rl;
+ rl->object_ref = o;
+
+ /** New line connects new point 1 and old point 1,
+ * this is a inner line separating newly generated triangles.
+ */
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[1];
+ rl->r = &rv[1];
+ rl->tl = rt1;
+ rl->tr = rt2;
+ rt1->rl[0] = rl;
+ rt2->rl[0] = rl;
+ rl->object_ref = o;
+
+ /* We now have one triangle closed. */
+ rt1->v[0] = rt->v[1];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = &rv[0];
+
+ /** New line connects new point 1 and old point 2,
+ * this is also a border line.
+ */
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[2];
+ rl->r = &rv[1];
+ rl->tl = rt2;
+ rl->tr = rt->rl[2]->tr == rt ? rt->rl[2]->tl : rt->rl[2]->tr;
+ rt2->rl[2] = rl;
+ rt2->rl[1] = rt->rl[1];
+ rl->object_ref = o;
+
+ /* Close the second triangle. */
+ rt2->v[0] = &rv[1];
+ rt2->v[1] = rt->v[1];
+ rt2->v[2] = rt->v[2];
+
+ lanpr_post_triangle(rt1, rt);
+ lanpr_post_triangle(rt2, rt);
+
+ v_count += 2;
+ t_count += 2;
+ continue;
+ }
+ 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);
+
+ 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);
+
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[0]);
+ rt->rl[0]->next = rt->rl[0]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[1]);
+ rt->rl[1]->next = rt->rl[1]->prev = 0;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rt1->rl[1] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[0];
+ rl->r = rt->v[2];
+ rl->tl = rt1;
+ rl->tr = rt->rl[1]->tl == rt ? rt->rl[1]->tr : rt->rl[1]->tl;
+ rt1->rl[2] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[2];
+ rl->r = &rv[1];
+ rl->tl = rt1;
+ rl->tr = rt2;
+ rt1->rl[0] = rl;
+ rt2->rl[0] = rl;
+ rl->object_ref = o;
+
+ rt1->v[0] = rt->v[2];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = &rv[0];
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[0];
+ rl->r = &rv[1];
+ rl->tl = rt2;
+ rl->tr = rt->rl[0]->tr == rt ? rt->rl[0]->tl : rt->rl[0]->tr;
+ rt2->rl[2] = rl;
+ rt2->rl[1] = rt->rl[2];
+ rl->object_ref = o;
+
+ rt2->v[0] = &rv[1];
+ rt2->v[1] = rt->v[2];
+ rt2->v[2] = rt->v[0];
+
+ lanpr_post_triangle(rt1, rt);
+ lanpr_post_triangle(rt2, rt);
+
+ v_count += 2;
+ t_count += 2;
+ continue;
+ }
+ 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);
+
+ 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);
+
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[1]);
+ rt->rl[1]->next = rt->rl[1]->prev = 0;
+ BLI_remlink(&rb->all_render_lines, (void *)rt->rl[2]);
+ rt->rl[2]->next = rt->rl[2]->prev = 0;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[1];
+ rl->r = &rv[0];
+ rl->tl = rt1;
+ rt1->rl[1] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = &rv[0];
+ rl->r = rt->v[0];
+ rl->tl = rt1;
+ rl->tr = rt->rl[2]->tl == rt ? rt->rl[2]->tr : rt->rl[2]->tl;
+ rt1->rl[2] = rl;
+ rl->object_ref = o;
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[0];
+ rl->r = &rv[1];
+ rl->tl = rt1;
+ rl->tr = rt2;
+ rt1->rl[0] = rl;
+ rt2->rl[0] = rl;
+ rl->object_ref = o;
+
+ rt1->v[0] = rt->v[0];
+ rt1->v[1] = &rv[1];
+ rt1->v[2] = &rv[0];
+
+ rl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ rls = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ BLI_addtail(&rb->all_render_lines, rl);
+ rl->l = rt->v[1];
+ rl->r = &rv[1];
+ rl->tl = rt2;
+ rl->tr = rt->rl[1]->tr == rt ? rt->rl[1]->tl : rt->rl[1]->tr;
+ rt2->rl[2] = rl;
+ rt2->rl[1] = rt->rl[0];
+ rl->object_ref = o;
+
+ rt2->v[0] = &rv[1];
+ rt2->v[1] = rt->v[0];
+ rt2->v[2] = rt->v[1];
+
+ lanpr_post_triangle(rt1, rt);
+ lanpr_post_triangle(rt2, rt);
+
+ v_count += 2;
+ t_count += 2;
+ continue;
+ }
+ break;
+ }
+ }
+ teln->element_count = t_count;
+ veln->element_count = v_count;
+ }
+}
+static void lanpr_perspective_division(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderVert *rv;
+ LANPR_RenderElementLinkNode *reln;
+ int i;
+
+ if (!rb->cam_is_persp) {
+ return;
+ }
+
+ for (reln = rb->vertex_buffer_pointers.first; reln; reln = reln->next) {
+ rv = reln->pointer;
+ for (i = 0; i < reln->element_count; i++) {
+ mul_v3db_db(rv[i].fbcoord, 1 / rv[i].fbcoord[3]);
+ rv[i].fbcoord[0] -= rb->shift_x * 2;
+ rv[i].fbcoord[1] -= rb->shift_y * 2;
+ }
+ }
+}
+
+static void lanpr_transform_render_vert(
+ BMVert *v, int index, LANPR_RenderVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4])
+{
+ double co[4];
+ LANPR_RenderVert *rv = &RvBuf[index];
+ copy_v3db_v3fl(co, v->co);
+ mul_v3_m4v3_db(rv->gloc, mv_mat, co);
+ mul_v4_m4v3_db(rv->fbcoord, mvp_mat, co);
+}
+
+static void lanpr_make_render_geometry_buffers_object(Object *o,
+ double (*mv_mat)[4],
+ double (*mvp_mat)[4],
+ LANPR_RenderBuffer *rb,
+ int override_usage)
+{
+ BMesh *bm;
+ BMVert *v;
+ BMFace *f;
+ BMEdge *e;
+ BMLoop *loop;
+ LANPR_RenderLine *rl;
+ LANPR_RenderTriangle *rt;
+ double new_mvp[4][4], new_mv[4][4], normal[4][4];
+ LANPR_RenderElementLinkNode *reln;
+ LANPR_RenderVert *orv;
+ LANPR_RenderLine *orl;
+ LANPR_RenderTriangle *ort;
+ FreestyleEdge *fe;
+ int CanFindFreestyle = 0;
+ int i;
+
+ int usage = override_usage ? override_usage : o->lanpr.usage;
+
+ if (usage == OBJECT_FEATURE_LINE_EXCLUDE) {
+ return;
+ }
+
+ if (o->type == OB_MESH) {
+
+ mul_m4db_m4db_m4fl_uniq(new_mvp, mvp_mat, o->obmat);
+ mul_m4db_m4db_m4fl_uniq(new_mv, mv_mat, o->obmat);
+
+ invert_m4_m4(o->imat, o->obmat);
+ transpose_m4(o->imat);
+ copy_m4d_m4(normal, o->imat);
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(o->data)));
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+ BM_mesh_bm_from_me(bm,
+ o->data,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false);
+ BM_mesh_triangulate(
+ bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL);
+ BM_mesh_normals_update(bm);
+ BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
+ CanFindFreestyle = 1;
+ }
+
+ orv = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderVert) * bm->totvert);
+ ort = mem_static_aquire(&rb->render_data_pool, bm->totface * rb->triangle_size);
+ orl = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine) * bm->totedge);
+
+ reln = list_append_pointer_static_sized(&rb->vertex_buffer_pointers,
+ &rb->render_data_pool,
+ orv,
+ sizeof(LANPR_RenderElementLinkNode));
+ reln->element_count = bm->totvert;
+ reln->object_ref = o;
+
+ reln = list_append_pointer_static_sized(&rb->line_buffer_pointers,
+ &rb->render_data_pool,
+ orl,
+ sizeof(LANPR_RenderElementLinkNode));
+ reln->element_count = bm->totedge;
+ reln->object_ref = o;
+
+ reln = list_append_pointer_static_sized(&rb->triangle_buffer_pointers,
+ &rb->render_data_pool,
+ ort,
+ sizeof(LANPR_RenderElementLinkNode));
+ reln->element_count = bm->totface;
+ reln->object_ref = o;
+
+ for (i = 0; i < bm->totvert; i++) {
+ v = BM_vert_at_index(bm, i);
+ lanpr_transform_render_vert(v, i, orv, new_mv, new_mvp);
+ }
+
+ rl = orl;
+ for (i = 0; i < bm->totedge; i++) {
+ e = BM_edge_at_index(bm, i);
+ if (CanFindFreestyle) {
+ fe = CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE);
+ if (fe->flag & FREESTYLE_EDGE_MARK) {
+ rl->flags |= LANPR_EDGE_FLAG_EDGE_MARK;
+ }
+ }
+ if (use_smooth_contour_modifier_contour) {
+ rl->edge_idx = i;
+ if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && BM_elem_flag_test(e->v2, BM_ELEM_SELECT)) {
+ rl->flags |= LANPR_EDGE_FLAG_CONTOUR;
+ }
+ }
+
+ rl->l = &orv[BM_elem_index_get(e->v1)];
+ rl->r = &orv[BM_elem_index_get(e->v2)];
+
+ rl->object_ref = o;
+
+ LANPR_RenderLineSegment *rls = mem_static_aquire(&rb->render_data_pool,
+ sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&rl->segments, rls);
+ if (usage == OBJECT_FEATURE_LINE_INHERENT) {
+ BLI_addtail(&rb->all_render_lines, rl);
+ }
+ rl++;
+ }
+
+ 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)];
+ rt->rl[0] = &orl[BM_elem_index_get(loop->e)];
+ loop = loop->next;
+ rt->v[1] = &orv[BM_elem_index_get(loop->v)];
+ rt->rl[1] = &orl[BM_elem_index_get(loop->e)];
+ loop = loop->next;
+ rt->v[2] = &orv[BM_elem_index_get(loop->v)];
+ rt->rl[2] = &orl[BM_elem_index_get(loop->e)];
+
+ rt->material_id = f->mat_nr;
+
+ add_v3_v3_db(rt->gc, rt->v[0]->fbcoord);
+ add_v3_v3_db(rt->gc, rt->v[1]->fbcoord);
+ add_v3_v3_db(rt->gc, rt->v[2]->fbcoord);
+ mul_v3db_db(rt->gc, 1.0f / 3.0f);
+
+ double gn[3];
+ copy_v3db_v3fl(gn, f->no);
+ mul_v3_mat3_m4v3_db(rt->gn, normal, gn);
+ normalize_v3_d(rt->gn);
+ lanpr_assign_render_line_with_triangle(rt);
+
+ rt = (LANPR_RenderTriangle *)(((unsigned char *)rt) + rb->triangle_size);
+ }
+
+ BM_mesh_free(bm);
+ }
+}
+
+int ED_lanpr_object_collection_usage_check(Collection *c, Object *o)
+{
+ CollectionChild *cc;
+ int object_is_used = (o->lanpr.usage == OBJECT_FEATURE_LINE_INCLUDE ||
+ o->lanpr.usage == OBJECT_FEATURE_LINE_INHERENT);
+
+ if (object_is_used && (c->flag & COLLECTION_CONFIGURED_FOR_LANPR) &&
+ (c->lanpr->flags & LANPR_LINE_LAYER_COLLECTION_FORCE) &&
+ (c->lanpr->usage != COLLECTION_FEATURE_LINE_INCLUDE)) {
+ if (BKE_collection_has_object_recursive(c, o)) {
+ if (c->lanpr->usage == COLLECTION_FEATURE_LINE_EXCLUDE) {
+ return OBJECT_FEATURE_LINE_EXCLUDE;
+ }
+ else if (c->lanpr->usage == COLLECTION_FEATURE_LINE_OCCLUSION_ONLY) {
+ return OBJECT_FEATURE_LINE_OCCLUSION_ONLY;
+ }
+ }
+ }
+
+ if (c->children.first == NULL) {
+ if (BKE_collection_has_object(c, o)) {
+ if (o->lanpr.usage == OBJECT_FEATURE_LINE_INHERENT) {
+ if ((c->flag & COLLECTION_CONFIGURED_FOR_LANPR) &&
+ (c->lanpr->usage == COLLECTION_FEATURE_LINE_OCCLUSION_ONLY)) {
+ return OBJECT_FEATURE_LINE_OCCLUSION_ONLY;
+ }
+ else if ((c->flag & COLLECTION_CONFIGURED_FOR_LANPR) &&
+ (c->lanpr->usage == COLLECTION_FEATURE_LINE_EXCLUDE)) {
+ return OBJECT_FEATURE_LINE_EXCLUDE;
+ }
+ else {
+ return OBJECT_FEATURE_LINE_INHERENT;
+ }
+ }
+ else {
+ return o->lanpr.usage;
+ }
+ }
+ else {
+ return OBJECT_FEATURE_LINE_INHERENT;
+ }
+ }
+
+ for (cc = c->children.first; cc; cc = cc->next) {
+ int result = ED_lanpr_object_collection_usage_check(cc->collection, o);
+ if (result > OBJECT_FEATURE_LINE_INHERENT) {
+ return result;
+ }
+ }
+
+ return OBJECT_FEATURE_LINE_INHERENT;
+}
+
+static void lanpr_make_render_geometry_buffers(
+ Depsgraph *depsgraph,
+ Scene *s,
+ Object *c /* Still use camera arg for convenience */,
+ LANPR_RenderBuffer *rb)
+{
+ double proj[4][4], view[4][4], result[4][4];
+ float inv[4][4];
+
+ /* lock becore accessing shared status data */
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+
+ memset(rb->material_pointers, 0, sizeof(void *) * 2048);
+
+ if (lanpr_share.viewport_camera_override) {
+ copy_m4d_m4(proj, lanpr_share.persp);
+ invert_m4_m4(inv, lanpr_share.viewinv);
+ copy_m4_m4_db(rb->view_projection, proj);
+ }
+ else {
+ Camera *cam = c->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) {
+ tmat_make_perspective_matrix_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
+ }
+ else if (cam->type == CAM_ORTHO) {
+ double w = cam->ortho_scale / 2;
+ tmat_make_ortho_matrix_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
+ }
+ invert_m4_m4(inv, c->obmat);
+ mul_m4db_m4db_m4fl_uniq(result, proj, inv);
+ copy_m4_m4_db(proj, result);
+ copy_m4_m4_db(rb->view_projection, proj);
+ }
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+
+ unit_m4_db(view);
+
+ BLI_listbase_clear(&rb->triangle_buffer_pointers);
+ BLI_listbase_clear(&rb->vertex_buffer_pointers);
+
+ DEG_OBJECT_ITER_BEGIN (depsgraph,
+ o,
+ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) {
+ int usage = ED_lanpr_object_collection_usage_check(s->master_collection, o);
+
+ lanpr_make_render_geometry_buffers_object(o, view, proj, rb, usage);
+ }
+ DEG_OBJECT_ITER_END;
+}
+
+#define INTERSECT_SORT_MIN_TO_MAX_3(ia, ib, ic, lst) \
+ { \
+ lst[0] = TNS_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] = TNS_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]))); \
+ }
+
+static LANPR_RenderLine *lanpr_another_edge(const LANPR_RenderTriangle *rt,
+ const LANPR_RenderVert *rv)
+{
+ if (rt->v[0] == rv) {
+ return rt->rl[1];
+ }
+ else if (rt->v[1] == rv) {
+ return rt->rl[2];
+ }
+ else if (rt->v[2] == rv) {
+ return rt->rl[0];
+ }
+ return 0;
+}
+static int lanpr_share_edge_direct(const LANPR_RenderTriangle *rt, const LANPR_RenderLine *rl)
+{
+ if (rt->rl[0] == rl || rt->rl[1] == rl || rt->rl[2] == rl) {
+ return 1;
+ }
+ return 0;
+}
+
+/** This is the main function to calculate
+ * the occlusion status between 1(one) triangle and 1(one) line.
+ * if returned 1, then from/to will carry the occludded segments
+ * in ratio from rl->l to rl->r. the line is later cutted with
+ * these two values.
+ */
+static int lanpr_triangle_line_imagespace_intersection_v2(SpinLock *UNUSED(spl),
+ const LANPR_RenderTriangle *rt,
+ const LANPR_RenderLine *rl,
+ const double *override_cam_loc,
+ const char override_cam_is_persp,
+ const double vp[4][4],
+ const double *camera_dir,
+ const double cam_shift_x,
+ const double cam_shift_y,
+ double *from,
+ double *to)
+{
+ double is[3] = {0};
+ int order[3];
+ int LCross = -1, RCross = -1;
+ int a, b, c;
+ int st_l = 0, st_r = 0;
+
+ double Lv[3];
+ double Rv[3];
+ double vd4[4];
+ double Cv[3];
+ double dot_l, dot_r, dot_la, dot_ra;
+ double dot_f;
+ double gloc[4], trans[4];
+ double cut = -1;
+
+ double *LFBC = rl->l->fbcoord, *RFBC = rl->r->fbcoord, *FBC0 = rt->v[0]->fbcoord,
+ *FBC1 = rt->v[1]->fbcoord, *FBC2 = rt->v[2]->fbcoord;
+
+ /* No potential overlapping, return early. */
+ if ((MAX3(FBC0[0], FBC1[0], FBC2[0]) < MIN2(LFBC[0], RFBC[0])) ||
+ (MIN3(FBC0[0], FBC1[0], FBC2[0]) > MAX2(LFBC[0], RFBC[0])) ||
+ (MAX3(FBC0[1], FBC1[1], FBC2[1]) < MIN2(LFBC[1], RFBC[1])) ||
+ (MIN3(FBC0[1], FBC1[1], FBC2[1]) > MAX2(LFBC[1], RFBC[1]))) {
+ return 0;
+ }
+
+ /* If the the line is one of the edge in the triangle, then it's not occludded. */
+ if (lanpr_share_edge_direct(rt, rl)) {
+ return 0;
+ }
+
+ /* If the line visually crosses one of the edge in the triangle */
+ a = lanpr_LineIntersectTest2d(LFBC, RFBC, FBC0, FBC1, &is[0]);
+ b = lanpr_LineIntersectTest2d(LFBC, RFBC, FBC1, FBC2, &is[1]);
+ c = lanpr_LineIntersectTest2d(LFBC, RFBC, FBC2, FBC0, &is[2]);
+
+ INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order);
+
+ sub_v3_v3v3_db(Lv, rl->l->gloc, rt->v[0]->gloc);
+ sub_v3_v3v3_db(Rv, rl->r->gloc, rt->v[0]->gloc);
+
+ copy_v3_v3_db(Cv, camera_dir);
+
+ if (override_cam_is_persp) {
+ copy_v3_v3_db(vd4, override_cam_loc);
+ }
+ else {
+ copy_v4_v4_db(vd4, override_cam_loc);
+ }
+ if (override_cam_is_persp) {
+ sub_v3_v3v3_db(Cv, vd4, rt->v[0]->gloc);
+ }
+
+ dot_l = dot_v3v3_db(Lv, rt->gn);
+ dot_r = dot_v3v3_db(Rv, rt->gn);
+ dot_f = dot_v3v3_db(Cv, rt->gn);
+
+ if (!dot_f) {
+ return 0;
+ }
+
+ if (!a && !b && !c) {
+ if (!(st_l = lanpr_point_triangle_relation(LFBC, FBC0, FBC1, FBC2)) &&
+ !(st_r = lanpr_point_triangle_relation(RFBC, FBC0, FBC1, FBC2))) {
+ return 0; /* not occluding */
+ }
+ }
+
+ st_l = lanpr_point_triangle_relation(LFBC, FBC0, FBC1, FBC2);
+ st_r = lanpr_point_triangle_relation(RFBC, FBC0, FBC1, FBC2);
+
+ dot_la = fabs(dot_l);
+ if (dot_la < DBL_EPSILON) {
+ dot_la = 0;
+ dot_l = 0;
+ }
+ dot_ra = fabs(dot_r);
+ if (dot_ra < DBL_EPSILON) {
+ dot_ra = 0;
+ dot_r = 0;
+ }
+ if (dot_l - dot_r == 0) {
+ cut = 100000;
+ }
+ else if (dot_l * dot_r <= 0) {
+ cut = dot_la / fabs(dot_l - dot_r);
+ }
+ else {
+ cut = fabs(dot_r + dot_l) / fabs(dot_l - dot_r);
+ cut = dot_ra > dot_la ? 1 - cut : cut;
+ }
+
+ if (override_cam_is_persp) {
+ interp_v3_v3v3_db(gloc, rl->l->gloc, rl->r->gloc, cut);
+ mul_v4_m4v3_db(trans, vp, gloc);
+ mul_v3db_db(trans, (1 / trans[3]));
+ }
+ else {
+ interp_v3_v3v3_db(trans, rl->l->fbcoord, rl->r->fbcoord, cut);
+ }
+ trans[0] -= cam_shift_x * 2;
+ trans[1] -= cam_shift_y * 2;
+
+ /* To accomodate k=0 and k=inf (vertical) lines. */
+ if (fabs(rl->l->fbcoord[0] - rl->r->fbcoord[0]) > fabs(rl->l->fbcoord[1] - rl->r->fbcoord[1])) {
+ cut = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], trans[0]);
+ }
+ else {
+ cut = tmat_get_linear_ratio(rl->l->fbcoord[1], rl->r->fbcoord[1], trans[1]);
+ }
+
+ if (st_l == 2) {
+ if (st_r == 2) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 1) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 0) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 0, RCross);
+ }
+ }
+ else if (st_l == 1) {
+ if (st_r == 2) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 1) {
+ INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross);
+ INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross);
+ }
+ else if (st_r == 0) {
+ INTERSECT_JUST_GREATER(is, order, DBL_TRIANGLE_LIM, RCross);
+ if (TNS_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 (TNS_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 (TNS_ABC(LCross) && is[LCross] > 0) {
+ INTERSECT_JUST_GREATER(is, order, is[LCross], RCross);
+ }
+ else {
+ INTERSECT_JUST_GREATER(is, order, is[LCross], LCross);
+ INTERSECT_JUST_GREATER(is, order, is[LCross], RCross);
+ }
+ }
+ }
+
+ double LF = dot_l * dot_f, RF = dot_r * dot_f;
+
+ if (LF <= 0 && RF <= 0 && (dot_l || dot_r)) {
+
+ *from = MAX2(0, is[LCross]);
+ *to = MIN2(1, is[RCross]);
+ if (*from >= *to) {
+ return 0;
+ }
+ /* printf("1 From %f to %f\n",*From, *To); */
+ return 1;
+ }
+ else if (LF >= 0 && RF <= 0 && (dot_l || dot_r)) {
+ *from = MAX2(cut, is[LCross]);
+ *to = MIN2(1, is[RCross]);
+ if (*from >= *to) {
+ return 0;
+ }
+ /* printf("2 From %f to %f\n",*From, *To); */
+ return 1;
+ }
+ else if (LF <= 0 && RF >= 0 && (dot_l || dot_r)) {
+ *from = MAX2(0, is[LCross]);
+ *to = MIN2(cut, is[RCross]);
+ if (*from >= *to) {
+ return 0;
+ }
+ /* printf("3 From %f to %f\n",*From, *To); */
+ return 1;
+ }
+ else
+ return 0;
+ return 1;
+}
+
+static LANPR_RenderLine *lanpr_triangle_share_edge(const LANPR_RenderTriangle *l,
+ const LANPR_RenderTriangle *r)
+{
+ if (l->rl[0] == r->rl[0]) {
+ return r->rl[0];
+ }
+ if (l->rl[0] == r->rl[1]) {
+ return r->rl[1];
+ }
+ if (l->rl[0] == r->rl[2]) {
+ return r->rl[2];
+ }
+ if (l->rl[1] == r->rl[0]) {
+ return r->rl[0];
+ }
+ if (l->rl[1] == r->rl[1]) {
+ return r->rl[1];
+ }
+ if (l->rl[1] == r->rl[2]) {
+ return r->rl[2];
+ }
+ if (l->rl[2] == r->rl[0]) {
+ return r->rl[0];
+ }
+ if (l->rl[2] == r->rl[1]) {
+ return r->rl[1];
+ }
+ if (l->rl[2] == r->rl[2]) {
+ return r->rl[2];
+ }
+ return 0;
+}
+static LANPR_RenderVert *lanpr_triangle_share_point(const LANPR_RenderTriangle *l,
+ const LANPR_RenderTriangle *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 0;
+}
+
+static LANPR_RenderVert *lanpr_triangle_line_intersection_test(LANPR_RenderBuffer *rb,
+ LANPR_RenderLine *rl,
+ LANPR_RenderTriangle *rt,
+ LANPR_RenderTriangle *testing,
+ LANPR_RenderVert *last)
+{
+ double Lv[3];
+ double Rv[3];
+ double dot_l, dot_r;
+ LANPR_RenderVert *result, *rv;
+ double gloc[3];
+ LANPR_RenderVert *l = rl->l, *r = rl->r;
+
+ for (rv = testing->intersecting_verts.first; rv; rv = rv->next) {
+ if (rv->intersecting_with == rt && rv->intersecting_line == rl) {
+ return rv;
+ }
+ }
+
+ sub_v3_v3v3_db(Lv, l->gloc, testing->v[0]->gloc);
+ sub_v3_v3v3_db(Rv, r->gloc, testing->v[0]->gloc);
+
+ dot_l = dot_v3v3_db(Lv, testing->gn);
+ dot_r = dot_v3v3_db(Rv, testing->gn);
+
+ if (dot_l * dot_r > 0 || (!dot_l && !dot_r)) {
+ return 0;
+ }
+
+ dot_l = fabs(dot_l);
+ dot_r = fabs(dot_r);
+
+ interp_v3_v3v3_db(gloc, l->gloc, r->gloc, dot_l / (dot_l + dot_r));
+
+ if (last && TNS_DOUBLE_CLOSE_ENOUGH(last->gloc[0], gloc[0]) &&
+ TNS_DOUBLE_CLOSE_ENOUGH(last->gloc[1], gloc[1]) &&
+ TNS_DOUBLE_CLOSE_ENOUGH(last->gloc[2], gloc[2])) {
+
+ last->intersecting_line2 = rl;
+ return NULL;
+ }
+
+ if (!(lanpr_point_inside_triangle3de(
+ gloc, testing->v[0]->gloc, testing->v[1]->gloc, testing->v[2]->gloc))) {
+ return NULL;
+ }
+
+ result = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderVert));
+
+ result->edge_used = 1;
+
+ /** Caution! BMVert* result->v is reused to save a intersecting render vert.
+ * this saves memory when the scene is very large.
+ */
+ result->v = (void *)r;
+
+ copy_v3_v3_db(result->gloc, gloc);
+
+ BLI_addtail(&testing->intersecting_verts, result);
+
+ return result;
+}
+static LANPR_RenderLine *lanpr_triangle_generate_intersection_line_only(
+ LANPR_RenderBuffer *rb, LANPR_RenderTriangle *rt, LANPR_RenderTriangle *testing)
+{
+ LANPR_RenderVert *l = 0, *r = 0;
+ LANPR_RenderVert **next = &l;
+ LANPR_RenderLine *result;
+ LANPR_RenderVert *E0T = 0;
+ LANPR_RenderVert *E1T = 0;
+ LANPR_RenderVert *E2T = 0;
+ LANPR_RenderVert *TE0 = 0;
+ LANPR_RenderVert *TE1 = 0;
+ LANPR_RenderVert *TE2 = 0;
+ double cl[3];
+
+ double ZMin, ZMax;
+ ZMax = rb->far_clip;
+ ZMin = rb->near_clip;
+ copy_v3_v3_db(cl, rb->camera_pos);
+ LANPR_RenderVert *share = lanpr_triangle_share_point(testing, rt);
+
+ if (share) {
+ LANPR_RenderVert *new_share;
+ LANPR_RenderLine *rl = lanpr_another_edge(rt, share);
+
+ l = new_share = mem_static_aquire(&rb->render_data_pool, (sizeof(LANPR_RenderVert)));
+
+ new_share->edge_used = 1;
+ new_share->v = (void *)
+ r; /* Caution! BMVert* result->v is reused to save a intersecting render vert. */
+ copy_v3_v3_db(new_share->gloc, share->gloc);
+
+ r = lanpr_triangle_line_intersection_test(rb, rl, rt, testing, 0);
+
+ if (r == NULL) {
+ rl = lanpr_another_edge(testing, share);
+ r = lanpr_triangle_line_intersection_test(rb, rl, testing, rt, 0);
+ if (r == NULL) {
+ return 0;
+ }
+ BLI_addtail(&testing->intersecting_verts, new_share);
+ }
+ else {
+ BLI_addtail(&rt->intersecting_verts, new_share);
+ }
+ }
+ else {
+ if (UNLIKELY(!rt->rl[0] || !rt->rl[1] || !rt->rl[2])) {
+ /** If we enter here, then there must be problems in culling,
+ * extremely rare condition where floating point precision can't handle.
+ */
+ return 0;
+ }
+ E0T = lanpr_triangle_line_intersection_test(rb, rt->rl[0], rt, testing, 0);
+ if (E0T && (!(*next))) {
+ (*next) = E0T;
+ (*next)->intersecting_line = rt->rl[0];
+ next = &r;
+ }
+ E1T = lanpr_triangle_line_intersection_test(rb, rt->rl[1], rt, testing, l);
+ if (E1T && (!(*next))) {
+ (*next) = E1T;
+ (*next)->intersecting_line = rt->rl[1];
+ next = &r;
+ }
+ if (!(*next)) {
+ E2T = lanpr_triangle_line_intersection_test(rb, rt->rl[2], rt, testing, l);
+ }
+ if (E2T && (!(*next))) {
+ (*next) = E2T;
+ (*next)->intersecting_line = rt->rl[2];
+ next = &r;
+ }
+
+ if (!(*next)) {
+ TE0 = lanpr_triangle_line_intersection_test(rb, testing->rl[0], testing, rt, l);
+ }
+ if (TE0 && (!(*next))) {
+ (*next) = TE0;
+ (*next)->intersecting_line = testing->rl[0];
+ next = &r;
+ }
+ if (!(*next)) {
+ TE1 = lanpr_triangle_line_intersection_test(rb, testing->rl[1], testing, rt, l);
+ }
+ if (TE1 && (!(*next))) {
+ (*next) = TE1;
+ (*next)->intersecting_line = testing->rl[1];
+ next = &r;
+ }
+ if (!(*next)) {
+ TE2 = lanpr_triangle_line_intersection_test(rb, testing->rl[2], testing, rt, l);
+ }
+ if (TE2 && (!(*next))) {
+ (*next) = TE2;
+ (*next)->intersecting_line = testing->rl[2];
+ next = &r;
+ }
+
+ if (!(*next)) {
+ return 0;
+ }
+ }
+ mul_v4_m4v3_db(l->fbcoord, rb->view_projection, l->gloc);
+ mul_v4_m4v3_db(r->fbcoord, rb->view_projection, r->gloc);
+ mul_v3db_db(l->fbcoord, (1 / l->fbcoord[3]));
+ mul_v3db_db(r->fbcoord, (1 / r->fbcoord[3]));
+
+ l->fbcoord[0] -= rb->shift_x * 2;
+ l->fbcoord[1] -= rb->shift_y * 2;
+ r->fbcoord[0] -= rb->shift_x * 2;
+ r->fbcoord[1] -= rb->shift_y * 2;
+
+ l->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(l->fbcoord[2]) * (ZMax - ZMin));
+ r->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(r->fbcoord[2]) * (ZMax - ZMin));
+
+ l->intersecting_with = rt;
+ r->intersecting_with = testing;
+
+ result = mem_static_aquire(&rb->render_data_pool, sizeof(LANPR_RenderLine));
+ result->l = l;
+ result->r = r;
+ result->tl = rt;
+ result->tr = testing;
+ LANPR_RenderLineSegment *rls = mem_static_aquire(&rb->render_data_pool,
+ sizeof(LANPR_RenderLineSegment));
+ BLI_addtail(&result->segments, rls);
+ BLI_addtail(&rb->all_render_lines, result);
+ result->flags |= LANPR_EDGE_FLAG_INTERSECTION;
+ list_append_pointer_static(&rb->intersection_lines, &rb->render_data_pool, result);
+ int r1, r2, c1, c2, row, col;
+ if (lanpr_get_line_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) {
+ for (row = r1; row != r2 + 1; row++) {
+ for (col = c1; col != c2 + 1; col++) {
+ lanpr_link_line_with_bounding_area(rb, &rb->initial_bounding_areas[row * 4 + col], result);
+ }
+ }
+ }
+
+ rb->intersection_count++;
+
+ return result;
+}
+static void lanpr_triangle_calculate_intersections_in_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_RenderTriangle *rt,
+ LANPR_BoundingArea *ba)
+{
+ LANPR_RenderTriangle *testing_triangle;
+ LinkData *lip, *next_lip;
+
+ double *FBC0 = rt->v[0]->fbcoord, *FBC1 = rt->v[1]->fbcoord, *FBC2 = rt->v[2]->fbcoord;
+
+ if (ba->child) {
+ lanpr_triangle_calculate_intersections_in_bounding_area(rb, rt, &ba->child[0]);
+ lanpr_triangle_calculate_intersections_in_bounding_area(rb, rt, &ba->child[1]);
+ lanpr_triangle_calculate_intersections_in_bounding_area(rb, rt, &ba->child[2]);
+ lanpr_triangle_calculate_intersections_in_bounding_area(rb, rt, &ba->child[3]);
+ return;
+ }
+
+ for (lip = ba->linked_triangles.first; lip; lip = next_lip) {
+ next_lip = lip->next;
+ testing_triangle = lip->data;
+ if (testing_triangle == rt || testing_triangle->testing == rt ||
+ lanpr_triangle_share_edge(rt, testing_triangle)) {
+
+ continue;
+ }
+
+ testing_triangle->testing = rt;
+ double *RFBC0 = testing_triangle->v[0]->fbcoord, *RFBC1 = testing_triangle->v[1]->fbcoord,
+ *RFBC2 = testing_triangle->v[2]->fbcoord;
+
+ if ((MIN3(FBC0[2], FBC1[2], FBC2[2]) > MAX3(RFBC0[2], RFBC1[2], RFBC2[2])) ||
+ (MAX3(FBC0[2], FBC1[2], FBC2[2]) < MIN3(RFBC0[2], RFBC1[2], RFBC2[2])) ||
+ (MIN3(FBC0[0], FBC1[0], FBC2[0]) > MAX3(RFBC0[0], RFBC1[0], RFBC2[0])) ||
+ (MAX3(FBC0[0], FBC1[0], FBC2[0]) < MIN3(RFBC0[0], RFBC1[0], RFBC2[0])) ||
+ (MIN3(FBC0[1], FBC1[1], FBC2[1]) > MAX3(RFBC0[1], RFBC1[1], RFBC2[1])) ||
+ (MAX3(FBC0[1], FBC1[1], FBC2[1]) < MIN3(RFBC0[1], RFBC1[1], RFBC2[1]))) {
+ continue;
+ }
+
+ lanpr_triangle_generate_intersection_line_only(rb, rt, testing_triangle);
+ }
+}
+
+static void lanpr_compute_view_vector(LANPR_RenderBuffer *rb)
+{
+ float direction[3] = {0, 0, 1};
+ float trans[3];
+ float inv[4][4];
+
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+ if (lanpr_share.viewport_camera_override) {
+ if (lanpr_share.camera_is_persp) {
+ invert_m4_m4(inv, lanpr_share.viewinv);
+ }
+ else {
+ quat_to_mat4(inv, lanpr_share.viewquat);
+ }
+ }
+ else {
+ invert_m4_m4(inv, rb->cam_obmat);
+ }
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+ transpose_m4(inv);
+ mul_v3_mat3_m4v3(trans, inv, direction);
+ copy_v3db_v3fl(rb->view_vector, trans);
+}
+
+static void lanpr_compute_scene_contours(LANPR_RenderBuffer *rb, const float threshold)
+{
+ double *view_vector = rb->view_vector;
+ double dot_1 = 0, dot_2 = 0;
+ double result;
+ int add = 0;
+ LANPR_RenderLine *rl;
+ int contour_count = 0;
+ int crease_count = 0;
+ int material_count = 0;
+
+ if (!rb->cam_is_persp) {
+ lanpr_compute_view_vector(rb);
+ }
+
+ for (rl = rb->all_render_lines.first; rl; rl = rl->next) {
+
+ add = 0;
+ dot_1 = 0;
+ dot_2 = 0;
+
+ if (rb->cam_is_persp) {
+ sub_v3_v3v3_db(view_vector, rl->l->gloc, rb->camera_pos);
+ }
+
+ if (use_smooth_contour_modifier_contour) {
+ if (rl->flags & LANPR_EDGE_FLAG_CONTOUR) {
+ add = 1;
+ }
+ }
+ else {
+ if (rl->tl) {
+ dot_1 = dot_v3v3_db(view_vector, rl->tl->gn);
+ }
+ else {
+ add = 1;
+ }
+ if (rl->tr) {
+ dot_2 = dot_v3v3_db(view_vector, rl->tr->gn);
+ }
+ else {
+ add = 1;
+ }
+ }
+
+ if (!add) {
+ if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
+ add = 1;
+ }
+ else if (dot_v3v3_db(rl->tl->gn, rl->tr->gn) < threshold) {
+ add = 2;
+ }
+ else if (rl->tl && rl->tr && rl->tl->material_id != rl->tr->material_id) {
+ add = 3;
+ }
+ }
+
+ if (add == 1) {
+ rl->flags |= LANPR_EDGE_FLAG_CONTOUR;
+ list_append_pointer_static(&rb->contours, &rb->render_data_pool, rl);
+ contour_count++;
+ }
+ else if (add == 2) {
+ rl->flags |= LANPR_EDGE_FLAG_CREASE;
+ list_append_pointer_static(&rb->crease_lines, &rb->render_data_pool, rl);
+ crease_count++;
+ }
+ else if (add == 3) {
+ rl->flags |= LANPR_EDGE_FLAG_MATERIAL;
+ list_append_pointer_static(&rb->material_lines, &rb->render_data_pool, rl);
+ material_count++;
+ }
+ if (rl->flags & LANPR_EDGE_FLAG_EDGE_MARK) {
+ /* no need to mark again */
+ add = 4;
+ list_append_pointer_static(&rb->edge_marks, &rb->render_data_pool, rl);
+ /* continue; */
+ }
+ if (add) {
+ int r1, r2, c1, c2, row, col;
+ if (lanpr_get_line_bounding_areas(rb, rl, &r1, &r2, &c1, &c2)) {
+ for (row = r1; row != r2 + 1; row++) {
+ for (col = c1; col != c2 + 1; col++) {
+ lanpr_link_line_with_bounding_area(rb, &rb->initial_bounding_areas[row * 4 + col], rl);
+ }
+ }
+ }
+ }
+
+ /* line count reserved for feature such as progress feedback */
+ }
+}
+
+/* Buffer operations */
+
+void ED_lanpr_destroy_render_data(LANPR_RenderBuffer *rb)
+{
+ if (rb == NULL) {
+ return;
+ }
+
+ ED_lanpr_render_buffer_cache_free(rb);
+
+ rb->contour_count = 0;
+ rb->contour_managed = 0;
+ rb->intersection_count = 0;
+ rb->intersection_managed = 0;
+ rb->material_line_count = 0;
+ rb->material_managed = 0;
+ rb->crease_count = 0;
+ rb->crease_managed = 0;
+ rb->edge_mark_count = 0;
+ rb->edge_mark_managed = 0;
+
+ BLI_listbase_clear(&rb->contours);
+ BLI_listbase_clear(&rb->intersection_lines);
+ BLI_listbase_clear(&rb->crease_lines);
+ BLI_listbase_clear(&rb->material_lines);
+ BLI_listbase_clear(&rb->edge_marks);
+ BLI_listbase_clear(&rb->all_render_lines);
+ BLI_listbase_clear(&rb->chains);
+
+ 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->render_data_pool.lock_mem);
+
+ mem_static_destroy(&rb->render_data_pool);
+}
+LANPR_RenderBuffer *ED_lanpr_create_render_buffer(Scene *s)
+{
+ /* Re-init render_buffer_shared */
+ if (lanpr_share.render_buffer_shared) {
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+ ED_lanpr_destroy_render_data(rb);
+ MEM_freeN(rb);
+ }
+
+ LANPR_RenderBuffer *rb = MEM_callocN(sizeof(LANPR_RenderBuffer), "LANPR render buffer");
+
+ lanpr_share.render_buffer_shared = rb;
+ if (lanpr_share.viewport_camera_override) {
+ copy_v3db_v3fl(rb->camera_pos, lanpr_share.camera_pos);
+ rb->cam_is_persp = lanpr_share.camera_is_persp;
+ rb->near_clip = lanpr_share.near_clip;
+ rb->far_clip = lanpr_share.far_clip;
+ rb->shift_x = rb->shift_y = 0.0f;
+ }
+ else {
+ Camera *c = s->camera->data;
+ copy_v3db_v3fl(rb->camera_pos, s->camera->obmat[3]);
+ copy_m4_m4(rb->cam_obmat, s->camera->obmat);
+ rb->cam_is_persp = (c->type == CAM_PERSP);
+ rb->near_clip = c->clip_start;
+ rb->far_clip = c->clip_end;
+ rb->shift_x = c->shiftx;
+ rb->shift_y = c->shifty;
+ }
+
+ rb->chaining_image_threshold = s->lanpr.chaining_image_threshold;
+ rb->chaining_geometry_threshold = s->lanpr.chaining_geometry_threshold;
+
+ BLI_spin_init(&rb->lock_task);
+ BLI_spin_init(&rb->render_data_pool.lock_mem);
+
+ if (!(lanpr_share.init_complete & LANPR_INIT_LOCKS)) {
+ BLI_spin_init(&lanpr_share.lock_loader);
+ BLI_spin_init(&lanpr_share.lock_render_status);
+ lanpr_share.init_complete |= LANPR_INIT_LOCKS;
+ }
+
+ return rb;
+}
+
+void ED_lanpr_calculation_set_flag(LANPR_RenderStatus flag)
+{
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+
+ if (flag == LANPR_RENDER_FINISHED && lanpr_share.flag_render_status == LANPR_RENDER_INCOMPELTE) {
+ ; /* Don't set the finished flag when it's canceled from any one of the thread.*/
+ }
+ else {
+ lanpr_share.flag_render_status = flag;
+ }
+
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+}
+
+bool ED_lanpr_calculation_flag_check(LANPR_RenderStatus flag)
+{
+ bool match;
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+ match = (lanpr_share.flag_render_status == flag);
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+ return match;
+}
+
+static int lanpr_max_occlusion_in_collections(Collection *c)
+{
+ CollectionChild *cc;
+ int max_occ = 0;
+ int max;
+ if (!(c->flag & COLLECTION_CONFIGURED_FOR_LANPR)) {
+ return 0;
+ }
+
+ if (c->lanpr->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS) {
+ max = MAX2(c->lanpr->level_start, c->lanpr->level_end);
+ }
+ else {
+ max = c->lanpr->level_start;
+ }
+ max_occ = MAX2(max, max_occ);
+
+ for (cc = c->children.first; cc; cc = cc->next) {
+ max = lanpr_max_occlusion_in_collections(cc->collection);
+ max_occ = MAX2(max, max_occ);
+ }
+
+ return max_occ;
+}
+static int lanpr_max_occlusion_in_targets(Depsgraph *depsgraph)
+{
+ int max_occ = 0;
+ int max;
+ Scene *s = DEG_get_evaluated_scene(depsgraph);
+
+ /* Objects */
+ DEG_OBJECT_ITER_BEGIN (depsgraph,
+ o,
+ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) {
+
+ ObjectLANPR *obl = &o->lanpr;
+ if (obl->target) {
+ if (obl->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS) {
+ max = MAX2(obl->level_start, obl->level_end);
+ }
+ else {
+ max = obl->level_start;
+ }
+ max_occ = MAX2(max, max_occ);
+ }
+ }
+ DEG_OBJECT_ITER_END;
+
+ /* Collections */
+ max = lanpr_max_occlusion_in_collections(s->master_collection);
+
+ max_occ = MAX2(max, max_occ);
+
+ return max_occ;
+}
+static int lanpr_get_max_occlusion_level(Depsgraph *dg)
+{
+ Scene *s = DEG_get_evaluated_scene(dg);
+ SceneLANPR *lanpr = s->id.orig_id ? &((Scene *)s->id.orig_id)->lanpr : &s->lanpr;
+ if (!strcmp(s->r.engine, RE_engine_id_BLENDER_LANPR)) {
+ /* Use the line layers in scene LANPR settings */
+ return ED_lanpr_max_occlusion_in_line_layers(lanpr);
+ }
+ else {
+ /* Other engines, use GPencil configurations */
+ return lanpr_max_occlusion_in_targets(dg);
+ }
+}
+
+static int lanpr_get_render_triangle_size(LANPR_RenderBuffer *rb, const Scene *s)
+{
+ if (rb->thread_count == 0) {
+ rb->thread_count = BKE_render_num_threads(&s->r);
+ }
+ return sizeof(LANPR_RenderTriangle) + (sizeof(LANPR_RenderLine *) * rb->thread_count);
+}
+
+int ED_lanpr_count_leveled_edge_segment_count(const ListBase *line_list, const LANPR_LineLayer *ll)
+{
+ LinkData *lip;
+ LANPR_RenderLine *rl;
+ LANPR_RenderLineSegment *rls;
+ int count = 0;
+ for (lip = line_list->first; lip; lip = lip->next) {
+ rl = lip->data;
+
+ for (rls = rl->segments.first; rls; rls = rls->next) {
+
+ if (!(ll->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS)) {
+ if (rls->occlusion == ll->level_start) {
+ count++;
+ }
+ }
+ else {
+ if (rls->occlusion >= ll->level_start && rls->occlusion <= ll->level_end) {
+ count++;
+ }
+ }
+ }
+ }
+ return count;
+}
+int lanpr_count_intersection_segment_count(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderLine *rl;
+ int count = 0;
+ for (rl = rb->intersection_lines.first; rl; rl = rl->next) {
+ count++;
+ }
+ return count;
+}
+void *ED_lanpr_make_leveled_edge_vertex_array(LANPR_RenderBuffer *UNUSED(rb),
+ const ListBase *line_list,
+ float *vertexArray,
+ float *normal_array,
+ float **next_normal,
+ const LANPR_LineLayer *ll,
+ const float componet_id)
+{
+ LinkData *lip;
+ LANPR_RenderLine *rl;
+ LANPR_RenderLineSegment *rls, *irls;
+ float *v = vertexArray;
+ float *N = normal_array;
+ for (lip = line_list->first; lip; lip = lip->next) {
+ rl = lip->data;
+
+ for (rls = rl->segments.first; rls; rls = rls->next) {
+ int use = 0;
+ if (!(ll->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS)) {
+ if (rls->occlusion == ll->level_start) {
+ use = 1;
+ }
+ }
+ else {
+ if (rls->occlusion >= ll->level_start && rls->occlusion <= ll->level_end) {
+ use = 1;
+ }
+ }
+
+ if (!use)
+ continue;
+
+ 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);
+ copy_v3_v3(&N[3], N);
+ }
+ N += 6;
+
+ CLAMP(rls->at, 0, 1);
+ if ((irls = rls->next) != NULL) {
+ CLAMP(irls->at, 0, 1);
+ }
+
+ *v = interpf(rl->r->fbcoord[0], rl->l->fbcoord[0], rls->at);
+ v++;
+ *v = interpf(rl->r->fbcoord[1], rl->l->fbcoord[1], rls->at);
+ v++;
+ *v = componet_id;
+ v++;
+ *v = interpf(rl->r->fbcoord[0], rl->l->fbcoord[0], irls ? irls->at : 1);
+ v++;
+ *v = interpf(rl->r->fbcoord[1], rl->l->fbcoord[1], irls ? irls->at : 1);
+ v++;
+ *v = componet_id;
+ v++;
+ }
+ }
+ *next_normal = N;
+ return v;
+}
+
+void lanpr_chain_generate_draw_command(LANPR_RenderBuffer *rb);
+
+#define TNS_BOUND_AREA_CROSSES(b1, b2) \
+ ((b1)[0] < (b2)[1] && (b1)[1] > (b2)[0] && (b1)[3] < (b2)[2] && (b1)[2] > (b2)[3])
+
+static void lanpr_make_initial_bounding_areas(LANPR_RenderBuffer *rb)
+{
+ int sp_w = 4; /* 20; */
+ int sp_h = 4; /* rb->H / (rb->W / sp_w); */
+ int row, col;
+ LANPR_BoundingArea *ba;
+ double span_w = (double)1 / sp_w * 2.0;
+ double span_h = (double)1 / sp_h * 2.0;
+
+ rb->tile_count_x = sp_w;
+ rb->tile_count_y = sp_h;
+ rb->width_per_tile = span_w;
+ rb->height_per_tile = span_h;
+
+ rb->bounding_area_count = sp_w * sp_h;
+ rb->initial_bounding_areas = mem_static_aquire(
+ &rb->render_data_pool, sizeof(LANPR_BoundingArea) * rb->bounding_area_count);
+
+ for (row = 0; row < sp_h; row++) {
+ for (col = 0; col < sp_w; col++) {
+ ba = &rb->initial_bounding_areas[row * 4 + col];
+
+ ba->l = span_w * col - 1.0;
+ ba->r = (col == sp_w - 1) ? 1.0 : (span_w * (col + 1) - 1.0);
+ ba->u = 1.0 - span_h * row;
+ ba->b = (row == sp_h - 1) ? -1.0 : (1.0 - span_h * (row + 1));
+
+ ba->cx = (ba->l + ba->r) / 2;
+ ba->cy = (ba->u + ba->b) / 2;
+
+ if (row) {
+ list_append_pointer_static(
+ &ba->up, &rb->render_data_pool, &rb->initial_bounding_areas[(row - 1) * 4 + col]);
+ }
+ if (col) {
+ list_append_pointer_static(
+ &ba->lp, &rb->render_data_pool, &rb->initial_bounding_areas[row * 4 + col - 1]);
+ }
+ if (row != sp_h - 1) {
+ list_append_pointer_static(
+ &ba->bp, &rb->render_data_pool, &rb->initial_bounding_areas[(row + 1) * 4 + col]);
+ }
+ if (col != sp_w - 1) {
+ list_append_pointer_static(
+ &ba->rp, &rb->render_data_pool, &rb->initial_bounding_areas[row * 4 + col + 1]);
+ }
+ }
+ }
+}
+static void lanpr_connect_new_bounding_areas(LANPR_RenderBuffer *rb, LANPR_BoundingArea *root)
+{
+ LANPR_BoundingArea *ba = root->child, *tba;
+ LinkData *lip, *lip2, *next_lip;
+ LANPR_StaticMemPool *mph = &rb->render_data_pool;
+
+ /* Inter-connection with newly created 4 child bounding areas. */
+ list_append_pointer_static_pool(mph, &ba[1].rp, &ba[0]);
+ list_append_pointer_static_pool(mph, &ba[0].lp, &ba[1]);
+ list_append_pointer_static_pool(mph, &ba[1].bp, &ba[2]);
+ list_append_pointer_static_pool(mph, &ba[2].up, &ba[1]);
+ list_append_pointer_static_pool(mph, &ba[2].rp, &ba[3]);
+ list_append_pointer_static_pool(mph, &ba[3].lp, &ba[2]);
+ list_append_pointer_static_pool(mph, &ba[3].up, &ba[0]);
+ list_append_pointer_static_pool(mph, &ba[0].bp, &ba[3]);
+
+ /** Connect 4 child bounding areas to other areas that are
+ * adjacent to their original parents */
+ for (lip = root->lp.first; lip; lip = lip->next) {
+
+ /** 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) {
+ list_append_pointer_static_pool(mph, &ba[1].lp, tba);
+ list_append_pointer_static_pool(mph, &tba->rp, &ba[1]);
+ }
+ if (ba[2].u > tba->b && ba[2].b < tba->u) {
+ list_append_pointer_static_pool(mph, &ba[2].lp, tba);
+ list_append_pointer_static_pool(mph, &tba->rp, &ba[2]);
+ }
+ }
+ for (lip = root->rp.first; lip; lip = lip->next) {
+ tba = lip->data;
+ if (ba[0].u > tba->b && ba[0].b < tba->u) {
+ list_append_pointer_static_pool(mph, &ba[0].rp, tba);
+ list_append_pointer_static_pool(mph, &tba->lp, &ba[0]);
+ }
+ if (ba[3].u > tba->b && ba[3].b < tba->u) {
+ list_append_pointer_static_pool(mph, &ba[3].rp, tba);
+ list_append_pointer_static_pool(mph, &tba->lp, &ba[3]);
+ }
+ }
+ for (lip = root->up.first; lip; lip = lip->next) {
+ tba = lip->data;
+ if (ba[0].r > tba->l && ba[0].l < tba->r) {
+ list_append_pointer_static_pool(mph, &ba[0].up, tba);
+ list_append_pointer_static_pool(mph, &tba->bp, &ba[0]);
+ }
+ if (ba[1].r > tba->l && ba[1].l < tba->r) {
+ list_append_pointer_static_pool(mph, &ba[1].up, tba);
+ list_append_pointer_static_pool(mph, &tba->bp, &ba[1]);
+ }
+ }
+ for (lip = root->bp.first; lip; lip = lip->next) {
+ tba = lip->data;
+ if (ba[2].r > tba->l && ba[2].l < tba->r) {
+ list_append_pointer_static_pool(mph, &ba[2].bp, tba);
+ list_append_pointer_static_pool(mph, &tba->up, &ba[2]);
+ }
+ if (ba[3].r > tba->l && ba[3].l < tba->r) {
+ list_append_pointer_static_pool(mph, &ba[3].bp, tba);
+ list_append_pointer_static_pool(mph, &tba->up, &ba[3]);
+ }
+ }
+
+ /** Then remove the parent bounding areas from
+ * their original adjacent areas. */
+ for (lip = root->lp.first; lip; lip = lip->next) {
+ for (lip2 = ((LANPR_BoundingArea *)lip->data)->rp.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ list_remove_pointer_item_no_free(&((LANPR_BoundingArea *)lip->data)->rp, lip2);
+ if (ba[1].u > tba->b && ba[1].b < tba->u) {
+ list_append_pointer_static_pool(mph, &tba->rp, &ba[1]);
+ }
+ if (ba[2].u > tba->b && ba[2].b < tba->u) {
+ list_append_pointer_static_pool(mph, &tba->rp, &ba[2]);
+ }
+ }
+ }
+ }
+ for (lip = root->rp.first; lip; lip = lip->next) {
+ for (lip2 = ((LANPR_BoundingArea *)lip->data)->lp.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ list_remove_pointer_item_no_free(&((LANPR_BoundingArea *)lip->data)->lp, lip2);
+ if (ba[0].u > tba->b && ba[0].b < tba->u) {
+ list_append_pointer_static_pool(mph, &tba->lp, &ba[0]);
+ }
+ if (ba[3].u > tba->b && ba[3].b < tba->u) {
+ list_append_pointer_static_pool(mph, &tba->lp, &ba[3]);
+ }
+ }
+ }
+ }
+ for (lip = root->up.first; lip; lip = lip->next) {
+ for (lip2 = ((LANPR_BoundingArea *)lip->data)->bp.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ list_remove_pointer_item_no_free(&((LANPR_BoundingArea *)lip->data)->bp, lip2);
+ if (ba[0].r > tba->l && ba[0].l < tba->r) {
+ list_append_pointer_static_pool(mph, &tba->up, &ba[0]);
+ }
+ if (ba[1].r > tba->l && ba[1].l < tba->r) {
+ list_append_pointer_static_pool(mph, &tba->up, &ba[1]);
+ }
+ }
+ }
+ }
+ for (lip = root->bp.first; lip; lip = lip->next) {
+ for (lip2 = ((LANPR_BoundingArea *)lip->data)->up.first; lip2; lip2 = next_lip) {
+ next_lip = lip2->next;
+ tba = lip2->data;
+ if (tba == root) {
+ list_remove_pointer_item_no_free(&((LANPR_BoundingArea *)lip->data)->up, lip2);
+ if (ba[2].r > tba->l && ba[2].l < tba->r) {
+ list_append_pointer_static_pool(mph, &tba->bp, &ba[2]);
+ }
+ if (ba[3].r > tba->l && ba[3].l < tba->r) {
+ list_append_pointer_static_pool(mph, &tba->bp, &ba[3]);
+ }
+ }
+ }
+ }
+
+ /* Finally clear parent's adjacent list. */
+ while (list_pop_pointer_no_free(&root->lp))
+ ;
+ while (list_pop_pointer_no_free(&root->rp))
+ ;
+ while (list_pop_pointer_no_free(&root->up))
+ ;
+ while (list_pop_pointer_no_free(&root->bp))
+ ;
+}
+static void lanpr_link_triangle_with_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_BoundingArea *root_ba,
+ LANPR_RenderTriangle *rt,
+ double *LRUB,
+ int recursive);
+static void lanpr_split_bounding_area(LANPR_RenderBuffer *rb, LANPR_BoundingArea *root)
+{
+ LANPR_BoundingArea *ba = mem_static_aquire(&rb->render_data_pool,
+ sizeof(LANPR_BoundingArea) * 4);
+ LANPR_RenderTriangle *rt;
+ LANPR_RenderLine *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;
+
+ lanpr_connect_new_bounding_areas(rb, root);
+
+ while ((rt = list_pop_pointer_no_free(&root->linked_triangles)) != NULL) {
+ LANPR_BoundingArea *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 (TNS_BOUND_AREA_CROSSES(b, &cba[0].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &cba[0], rt, b, 0);
+ }
+ if (TNS_BOUND_AREA_CROSSES(b, &cba[1].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &cba[1], rt, b, 0);
+ }
+ if (TNS_BOUND_AREA_CROSSES(b, &cba[2].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &cba[2], rt, b, 0);
+ }
+ if (TNS_BOUND_AREA_CROSSES(b, &cba[3].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &cba[3], rt, b, 0);
+ }
+ }
+
+ while ((rl = list_pop_pointer_no_free(&root->linked_lines)) != NULL) {
+ lanpr_link_line_with_bounding_area(rb, root, rl);
+ }
+
+ rb->bounding_area_count += 3;
+}
+static int lanpr_line_crosses_bounding_area(LANPR_RenderBuffer *UNUSED(fb),
+ double l[2],
+ double r[2],
+ LANPR_BoundingArea *ba)
+{
+ double vx, vy;
+ double converted[4];
+ double c1, c;
+
+ if (((converted[0] = (double)ba->l) > MAX2(l[0], r[0])) ||
+ ((converted[1] = (double)ba->r) < MIN2(l[0], r[0])) ||
+ ((converted[2] = (double)ba->b) > MAX2(l[1], r[1])) ||
+ ((converted[3] = (double)ba->u) < MIN2(l[1], r[1]))) {
+ return 0;
+ }
+
+ vx = l[0] - r[0];
+ vy = l[1] - r[1];
+
+ c1 = vx * (converted[2] - l[1]) - vy * (converted[0] - l[0]);
+ c = c1;
+
+ c1 = vx * (converted[2] - l[1]) - vy * (converted[1] - l[0]);
+ if (c1 * c <= 0) {
+ return 1;
+ }
+ else {
+ c = c1;
+ }
+
+ c1 = vx * (converted[3] - l[1]) - vy * (converted[0] - l[0]);
+ if (c1 * c <= 0) {
+ return 1;
+ }
+ else {
+ c = c1;
+ }
+
+ c1 = vx * (converted[3] - l[1]) - vy * (converted[1] - l[0]);
+ if (c1 * c <= 0) {
+ return 1;
+ }
+ else {
+ c = c1;
+ }
+
+ return 0;
+}
+static int lanpr_triangle_covers_bounding_area(LANPR_RenderBuffer *fb,
+ LANPR_RenderTriangle *rt,
+ LANPR_BoundingArea *ba)
+{
+ double p1[2], p2[2], p3[2], p4[2];
+ double *FBC1 = rt->v[0]->fbcoord, *FBC2 = rt->v[1]->fbcoord, *FBC3 = rt->v[2]->fbcoord;
+
+ p3[0] = p1[0] = (double)ba->l;
+ p2[1] = p1[1] = (double)ba->b;
+ p2[0] = p4[0] = (double)ba->r;
+ p3[1] = p4[1] = (double)ba->u;
+
+ if ((FBC1[0] >= p1[0] && FBC1[0] <= p2[0] && FBC1[1] >= p1[1] && FBC1[1] <= p3[1]) ||
+ (FBC2[0] >= p1[0] && FBC2[0] <= p2[0] && FBC2[1] >= p1[1] && FBC2[1] <= p3[1]) ||
+ (FBC3[0] >= p1[0] && FBC3[0] <= p2[0] && FBC3[1] >= p1[1] && FBC3[1] <= p3[1])) {
+ return 1;
+ }
+
+ if (ED_lanpr_point_inside_triangled(p1, FBC1, FBC2, FBC3) ||
+ ED_lanpr_point_inside_triangled(p2, FBC1, FBC2, FBC3) ||
+ ED_lanpr_point_inside_triangled(p3, FBC1, FBC2, FBC3) ||
+ ED_lanpr_point_inside_triangled(p4, FBC1, FBC2, FBC3)) {
+ return 1;
+ }
+
+ if ((lanpr_line_crosses_bounding_area(fb, FBC1, FBC2, ba)) ||
+ (lanpr_line_crosses_bounding_area(fb, FBC2, FBC3, ba)) ||
+ (lanpr_line_crosses_bounding_area(fb, FBC3, FBC1, ba))) {
+ return 1;
+ }
+
+ return 0;
+}
+static void lanpr_link_triangle_with_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_BoundingArea *root_ba,
+ LANPR_RenderTriangle *rt,
+ double *LRUB,
+ int recursive)
+{
+ if (!lanpr_triangle_covers_bounding_area(rb, rt, root_ba)) {
+ return;
+ }
+ if (root_ba->child == NULL) {
+ list_append_pointer_static_pool(&rb->render_data_pool, &root_ba->linked_triangles, rt);
+ root_ba->triangle_count++;
+ if (root_ba->triangle_count > 200 && recursive) {
+ lanpr_split_bounding_area(rb, root_ba);
+ }
+ if (recursive && rb->use_intersections) {
+ lanpr_triangle_calculate_intersections_in_bounding_area(rb, rt, root_ba);
+ }
+ }
+ else {
+ LANPR_BoundingArea *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 (TNS_BOUND_AREA_CROSSES(B1, &ba[0].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &ba[0], rt, B1, recursive);
+ }
+ if (TNS_BOUND_AREA_CROSSES(B1, &ba[1].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &ba[1], rt, B1, recursive);
+ }
+ if (TNS_BOUND_AREA_CROSSES(B1, &ba[2].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &ba[2], rt, B1, recursive);
+ }
+ if (TNS_BOUND_AREA_CROSSES(B1, &ba[3].l)) {
+ lanpr_link_triangle_with_bounding_area(rb, &ba[3], rt, B1, recursive);
+ }
+ }
+}
+static void lanpr_link_line_with_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_BoundingArea *root_ba,
+ LANPR_RenderLine *rl)
+{
+ if (root_ba->child == NULL) {
+ list_append_pointer_static_pool(&rb->render_data_pool, &root_ba->linked_lines, rl);
+ }
+ else {
+ if (lanpr_line_crosses_bounding_area(rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[0])) {
+ lanpr_link_line_with_bounding_area(rb, &root_ba->child[0], rl);
+ }
+ if (lanpr_line_crosses_bounding_area(rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[1])) {
+ lanpr_link_line_with_bounding_area(rb, &root_ba->child[1], rl);
+ }
+ if (lanpr_line_crosses_bounding_area(rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[2])) {
+ lanpr_link_line_with_bounding_area(rb, &root_ba->child[2], rl);
+ }
+ if (lanpr_line_crosses_bounding_area(rb, rl->l->fbcoord, rl->r->fbcoord, &root_ba->child[3])) {
+ lanpr_link_line_with_bounding_area(rb, &root_ba->child[3], rl);
+ }
+ }
+}
+static int lanpr_get_triangle_bounding_areas(LANPR_RenderBuffer *rb,
+ LANPR_RenderTriangle *rt,
+ int *rowbegin,
+ int *rowend,
+ int *colbegin,
+ int *colend)
+{
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ double b[4];
+
+ if (!rt->v[0] || !rt->v[1] || !rt->v[2]) {
+ return 0;
+ }
+
+ b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
+ b[2] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[3] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+
+ if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
+ return 0;
+ }
+
+ (*colbegin) = (int)((b[0] + 1.0) / sp_w);
+ (*colend) = (int)((b[1] + 1.0) / sp_w);
+ (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1;
+ (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1;
+
+ if ((*colend) >= rb->tile_count_x) {
+ (*colend) = rb->tile_count_x - 1;
+ }
+ if ((*rowend) >= rb->tile_count_y) {
+ (*rowend) = rb->tile_count_y - 1;
+ }
+ if ((*colbegin) < 0) {
+ (*colbegin) = 0;
+ }
+ if ((*rowbegin) < 0) {
+ (*rowbegin) = 0;
+ }
+
+ return 1;
+}
+static int lanpr_get_line_bounding_areas(LANPR_RenderBuffer *rb,
+ LANPR_RenderLine *rl,
+ int *rowbegin,
+ int *rowend,
+ int *colbegin,
+ int *colend)
+{
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ double b[4];
+
+ if (!rl->l || !rl->r) {
+ return 0;
+ }
+
+ if (rl->l->fbcoord[0] != rl->l->fbcoord[0] || rl->r->fbcoord[0] != rl->r->fbcoord[0]) {
+ return 0;
+ }
+
+ b[0] = MIN2(rl->l->fbcoord[0], rl->r->fbcoord[0]);
+ b[1] = MAX2(rl->l->fbcoord[0], rl->r->fbcoord[0]);
+ b[2] = MIN2(rl->l->fbcoord[1], rl->r->fbcoord[1]);
+ b[3] = MAX2(rl->l->fbcoord[1], rl->r->fbcoord[1]);
+
+ if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
+ return 0;
+ }
+
+ (*colbegin) = (int)((b[0] + 1.0) / sp_w);
+ (*colend) = (int)((b[1] + 1.0) / sp_w);
+ (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1;
+ (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1;
+
+ /* It's possible that the line stretches too much out to the side, resulting negative value */
+ if ((*rowend) < (*rowbegin)) {
+ (*rowend) = rb->tile_count_y - 1;
+ }
+
+ if ((*colend) < (*colbegin)) {
+ (*colend) = rb->tile_count_x - 1;
+ }
+
+ CLAMP((*colbegin), 0, rb->tile_count_x - 1);
+ CLAMP((*rowbegin), 0, rb->tile_count_y - 1);
+ CLAMP((*colend), 0, rb->tile_count_x - 1);
+ CLAMP((*rowend), 0, rb->tile_count_y - 1);
+
+ return 1;
+}
+LANPR_BoundingArea *ED_lanpr_get_point_bounding_area(LANPR_RenderBuffer *rb, double x, double y)
+{
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ int col, row;
+
+ if (x > 1 || x < -1 || y > 1 || y < -1) {
+ return 0;
+ }
+
+ col = (int)((x + 1.0) / sp_w);
+ row = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1;
+
+ if (col >= rb->tile_count_x) {
+ col = rb->tile_count_x - 1;
+ }
+ if (row >= rb->tile_count_y) {
+ row = rb->tile_count_y - 1;
+ }
+ if (col < 0) {
+ col = 0;
+ }
+ if (row < 0) {
+ row = 0;
+ }
+
+ return &rb->initial_bounding_areas[row * 4 + col];
+}
+static LANPR_BoundingArea *lanpr_get_point_bounding_area_recursive(LANPR_BoundingArea *ba,
+ double x,
+ double y)
+{
+ if (ba->child == NULL) {
+ return ba;
+ }
+ else {
+#define IN_BOUND(i, x, y) \
+ ba->child[i].l <= x && ba->child[i].r >= x && ba->child[i].b <= y && ba->child[i].u >= y
+
+ if (IN_BOUND(0, x, y)) {
+ return lanpr_get_point_bounding_area_recursive(&ba->child[0], x, y);
+ }
+ else if (IN_BOUND(1, x, y)) {
+ return lanpr_get_point_bounding_area_recursive(&ba->child[1], x, y);
+ }
+ else if (IN_BOUND(2, x, y)) {
+ return lanpr_get_point_bounding_area_recursive(&ba->child[2], x, y);
+ }
+ else if (IN_BOUND(3, x, y)) {
+ return lanpr_get_point_bounding_area_recursive(&ba->child[3], x, y);
+ }
+ }
+ return NULL;
+}
+LANPR_BoundingArea *ED_lanpr_get_point_bounding_area_deep(LANPR_RenderBuffer *rb,
+ double x,
+ double y)
+{
+ LANPR_BoundingArea *ba;
+ if ((ba = ED_lanpr_get_point_bounding_area(rb, x, y)) != NULL) {
+ return lanpr_get_point_bounding_area_recursive(ba, x, y);
+ }
+ return NULL;
+}
+
+static void lanpr_add_triangles(LANPR_RenderBuffer *rb)
+{
+ LANPR_RenderElementLinkNode *reln;
+ LANPR_RenderTriangle *rt;
+ int i, lim;
+ int x1, x2, y1, y2;
+ int r, co;
+
+ for (reln = rb->triangle_buffer_pointers.first; reln; reln = reln->next) {
+ rt = reln->pointer;
+ lim = reln->element_count;
+ for (i = 0; i < lim; i++) {
+ if (rt->cull_status) {
+ rt = (void *)(((unsigned char *)rt) + rb->triangle_size);
+ continue;
+ }
+ if (lanpr_get_triangle_bounding_areas(rb, rt, &y1, &y2, &x1, &x2)) {
+ for (co = x1; co <= x2; co++) {
+ for (r = y1; r <= y2; r++) {
+ lanpr_link_triangle_with_bounding_area(
+ rb, &rb->initial_bounding_areas[r * 4 + co], rt, 0, 1);
+ }
+ }
+ } /* else throw away. */
+ rt = (void *)(((unsigned char *)rt) + rb->triangle_size);
+ }
+ }
+}
+
+/** This march along one render line in image space and
+ * get the next bounding area the line is crossing. */
+static LANPR_BoundingArea *lanpr_get_next_bounding_area(LANPR_BoundingArea *this,
+ LANPR_RenderLine *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;
+ LANPR_BoundingArea *ba;
+ LinkData *lip;
+
+ /* 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 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], rx);
+ r2 = tmat_get_linear_ratio(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) {
+ for (lip = this->rp.first; lip; lip = lip->next) {
+ 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 {
+ for (lip = this->up.first; lip; lip = lip->next) {
+ 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 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], rx);
+ r2 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], bx);
+ if (MIN2(r1, r2) > 1) {
+ return 0;
+ }
+ if (r1 <= r2) {
+ for (lip = this->rp.first; lip; lip = lip->next) {
+ ba = lip->data;
+ if (ba->u >= ry && ba->b < ry) {
+ *next_x = rx;
+ *next_y = ry;
+ return ba;
+ }
+ }
+ }
+ else {
+ for (lip = this->bp.first; lip; lip = lip->next) {
+ 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 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], this->r);
+ if (r1 > 1) {
+ return 0;
+ }
+ for (lip = this->rp.first; lip; lip = lip->next) {
+ 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 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], lx);
+ r2 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], ux);
+ if (MIN2(r1, r2) > 1) {
+ return 0;
+ }
+ if (r1 <= r2) {
+ for (lip = this->lp.first; lip; lip = lip->next) {
+ ba = lip->data;
+ if (ba->u >= ly && ba->b < ly) {
+ *next_x = lx;
+ *next_y = ly;
+ return ba;
+ }
+ }
+ }
+ else {
+ for (lip = this->up.first; lip; lip = lip->next) {
+ 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 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], lx);
+ r2 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], bx);
+ if (MIN2(r1, r2) > 1) {
+ return 0;
+ }
+ if (r1 <= r2) {
+ for (lip = this->lp.first; lip; lip = lip->next) {
+ ba = lip->data;
+ if (ba->u >= ly && ba->b < ly) {
+ *next_x = lx;
+ *next_y = ly;
+ return ba;
+ }
+ }
+ }
+ else {
+ for (lip = this->bp.first; lip; lip = lip->next) {
+ ba = lip->data;
+ if (ba->r >= bx && ba->l < bx) {
+ *next_x = bx;
+ *next_y = by;
+ return ba;
+ }
+ }
+ }
+ }
+ /* Again, horizontal */
+ else {
+ r1 = tmat_get_linear_ratio(rl->l->fbcoord[0], rl->r->fbcoord[0], this->l);
+ if (r1 > 1) {
+ return 0;
+ }
+ for (lip = this->lp.first; lip; lip = lip->next) {
+ 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 = tmat_get_linear_ratio(rl->l->fbcoord[1], rl->r->fbcoord[1], this->u);
+ if (r1 > 1) {
+ return 0;
+ }
+ for (lip = this->up.first; lip; lip = lip->next) {
+ 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 = tmat_get_linear_ratio(rl->l->fbcoord[1], rl->r->fbcoord[1], this->b);
+ if (r1 > 1) {
+ return 0;
+ }
+ for (lip = this->bp.first; lip; lip = lip->next) {
+ ba = lip->data;
+ if (ba->r > x && ba->l <= x) {
+ *next_x = x;
+ *next_y = this->b;
+ return ba;
+ }
+ }
+ }
+ else
+ return 0; /* segment has no length */
+ }
+ return 0;
+}
+
+static LANPR_BoundingArea *lanpr_get_bounding_area(LANPR_RenderBuffer *rb, double x, double y)
+{
+ LANPR_BoundingArea *iba;
+ double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
+ int c = (int)((x + 1.0) / sp_w);
+ int r = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1;
+ if (r < 0) {
+ r = 0;
+ }
+ if (c < 0) {
+ c = 0;
+ }
+ if (r >= rb->tile_count_y) {
+ r = rb->tile_count_y - 1;
+ }
+ if (c >= rb->tile_count_x) {
+ c = rb->tile_count_x - 1;
+ }
+
+ iba = &rb->initial_bounding_areas[r * 4 + c];
+ while (iba->child) {
+ if (x > iba->cx) {
+ if (y > iba->cy) {
+ iba = &iba->child[0];
+ }
+ else {
+ iba = &iba->child[3];
+ }
+ }
+ else {
+ if (y > iba->cy) {
+ iba = &iba->child[1];
+ }
+ else {
+ iba = &iba->child[2];
+ }
+ }
+ }
+ return iba;
+}
+static LANPR_BoundingArea *lanpr_get_first_possible_bounding_area(LANPR_RenderBuffer *rb,
+ LANPR_RenderLine *rl)
+{
+ LANPR_BoundingArea *iba;
+ double data[2] = {rl->l->fbcoord[0], rl->l->fbcoord[1]};
+ double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1};
+ double r = 1, sr = 1;
+
+ if (data[0] > -1 && data[0] < 1 && data[1] > -1 && data[1] < 1) {
+ return lanpr_get_bounding_area(rb, data[0], data[1]);
+ }
+ else {
+ if ((lanpr_LineIntersectTest2d(rl->l->fbcoord, rl->r->fbcoord, LU, RU, &sr) && sr < r &&
+ sr > 0) ||
+ (lanpr_LineIntersectTest2d(rl->l->fbcoord, rl->r->fbcoord, LB, RB, &sr) && sr < r &&
+ sr > 0) ||
+ (lanpr_LineIntersectTest2d(rl->l->fbcoord, rl->r->fbcoord, LB, LU, &sr) && sr < r &&
+ sr > 0) ||
+ (lanpr_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 lanpr_get_bounding_area(rb, data[0], data[1]);
+ }
+
+ return iba;
+}
+
+/* Calculations */
+
+/** Parent thread locking should be done before this very function is called. */
+int ED_lanpr_compute_feature_lines_internal(Depsgraph *depsgraph, const int intersectons_only)
+{
+ LANPR_RenderBuffer *rb;
+ Scene *s = DEG_get_evaluated_scene(depsgraph);
+ SceneLANPR *lanpr = &s->lanpr;
+ int is_lanpr_engine = !strcmp(s->r.engine, RE_engine_id_BLENDER_LANPR);
+
+ if (!is_lanpr_engine && (lanpr->flags & LANPR_ENABLED) == 0) {
+ /* Release lock when early return. */
+ BLI_spin_unlock(&lanpr_share.lock_loader);
+ return OPERATOR_CANCELLED;
+ }
+
+ rb = ED_lanpr_create_render_buffer(s);
+
+ lanpr_share.render_buffer_shared = rb;
+
+ rb->w = s->r.xsch;
+ rb->h = s->r.ysch;
+ rb->use_intersections = (lanpr->flags & LANPR_USE_INTERSECTIONS);
+
+ rb->triangle_size = lanpr_get_render_triangle_size(rb, s);
+
+ rb->max_occlusion_level = lanpr_get_max_occlusion_level(depsgraph);
+
+ ED_lanpr_update_render_progress("LANPR: Loading geometries.");
+
+ lanpr_make_render_geometry_buffers(depsgraph, s, s->camera, rb);
+
+ /** We had everything we need,
+ * Unlock parent thread, it's safe to run independently from now. */
+ BLI_spin_unlock(&lanpr_share.lock_loader);
+
+ lanpr_compute_view_vector(rb);
+ lanpr_cull_triangles(rb);
+
+ lanpr_perspective_division(rb);
+
+ lanpr_make_initial_bounding_areas(rb);
+
+ if (!intersectons_only) {
+ lanpr_compute_scene_contours(rb, lanpr->crease_threshold);
+ }
+
+ ED_lanpr_update_render_progress("LANPR: Computing intersections.");
+
+ lanpr_add_triangles(rb);
+
+ ED_lanpr_update_render_progress("LANPR: Computing line occlusion.");
+
+ if (!intersectons_only) {
+ lanpr_calculate_line_occlusion_begin(rb);
+ }
+
+ ED_lanpr_update_render_progress("LANPR: Chaining.");
+
+ /* When not using LANPR engine, chaining is forced in order to generate data for GPencil. */
+ if (((lanpr->flags & LANPR_USE_CHAINING) || !is_lanpr_engine) && (!intersectons_only)) {
+ float t_image = s->lanpr.chaining_image_threshold;
+ float t_geom = s->lanpr.chaining_geometry_threshold;
+
+ ED_lanpr_NO_THREAD_chain_feature_lines(rb);
+
+ if (is_lanpr_engine) {
+ /* Enough with it. We can provide an option after we have LANPR internal smoothing */
+ ED_lanpr_calculation_set_flag(LANPR_RENDER_FINISHED);
+ return OPERATOR_FINISHED;
+ }
+
+ /* Below are simply for better GPencil experience. */
+
+ ED_lanpr_split_chains_for_fixed_occlusion(rb);
+
+ if (t_image < FLT_EPSILON && t_geom < FLT_EPSILON) {
+ t_geom = 0.0f;
+ t_image = 0.01f;
+ }
+
+ ED_lanpr_connect_chains(rb, 1);
+ ED_lanpr_connect_chains(rb, 0);
+
+ /* This configuration ensures there won't be accidental lost of short segments */
+ ED_lanpr_discard_short_chains(rb, MIN3(t_image, t_geom, 0.01f) - FLT_EPSILON);
+ }
+
+ ED_lanpr_calculation_set_flag(LANPR_RENDER_FINISHED);
+
+ return OPERATOR_FINISHED;
+}
+
+typedef struct LANPR_FeatureLineWorker {
+ Depsgraph *dg;
+ int intersection_only;
+} LANPR_FeatureLineWorker;
+
+static void lanpr_compute_feature_lines_worker(TaskPool *__restrict UNUSED(pool),
+ LANPR_FeatureLineWorker *worker_data)
+{
+ ED_lanpr_compute_feature_lines_internal(worker_data->dg, worker_data->intersection_only);
+}
+
+void ED_lanpr_compute_feature_lines_background(Depsgraph *dg, const int intersection_only)
+{
+ TaskPool *tp_read;
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+ tp_read = lanpr_share.background_render_task;
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+
+ /* If the calculation is already started then bypass it. */
+ if (!ED_lanpr_calculation_flag_check(LANPR_RENDER_IDLE)) {
+ /* Release lock when early return. */
+ BLI_spin_unlock(&lanpr_share.lock_loader);
+ return;
+ }
+
+ if (tp_read) {
+ BLI_task_pool_free(lanpr_share.background_render_task);
+ lanpr_share.background_render_task = NULL;
+ }
+
+ ED_lanpr_calculation_set_flag(LANPR_RENDER_RUNNING);
+
+ LANPR_FeatureLineWorker *flw = MEM_callocN(sizeof(LANPR_FeatureLineWorker), "Task Pool");
+
+ flw->dg = dg;
+ flw->intersection_only = intersection_only;
+
+ TaskPool *tp = BLI_task_pool_create_background(flw, TASK_PRIORITY_HIGH);
+ BLI_spin_lock(&lanpr_share.lock_render_status);
+ lanpr_share.background_render_task = tp;
+ BLI_spin_unlock(&lanpr_share.lock_render_status);
+
+ BLI_task_pool_push(tp, (TaskRunFunction)lanpr_compute_feature_lines_worker, flw, true, NULL);
+}
+
+static bool lanpr_camera_exists(bContext *c)
+{
+ Scene *s = CTX_data_scene(c);
+ return s->camera ? true : false;
+}
+static int lanpr_compute_feature_lines_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneLANPR *lanpr = &scene->lanpr;
+ int result;
+ int is_lanpr_engine = !strcmp(scene->r.engine, RE_engine_id_BLENDER_LANPR);
+
+ if (!is_lanpr_engine && (lanpr->flags & LANPR_ENABLED) == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (scene->camera == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "There is no active camera in this scene!");
+ printf("LANPR Warning: There is no active camera in this scene!\n");
+ return OPERATOR_FINISHED;
+ }
+
+ int intersections_only = (is_lanpr_engine && lanpr->master_mode != LANPR_MASTER_MODE_SOFTWARE);
+
+ /** Lock caller thread before calling feature line computation.
+ * This worker is not a background task, so we don't need to try another lock
+ * to wait for the worker to finish. The lock will be released in the compute function.
+ */
+ BLI_spin_lock(&lanpr_share.lock_loader);
+
+ result = ED_lanpr_compute_feature_lines_internal(CTX_data_depsgraph_pointer(C),
+ intersections_only);
+
+ ED_lanpr_rebuild_all_command(scene);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+
+ return result;
+}
+static void lanpr_compute_feature_lines_cancel(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return;
+}
+
+void SCENE_OT_lanpr_calculate_feature_lines(wmOperatorType *ot)
+{
+
+ /* identifiers */
+ ot->name = "Calculate Feature Lines";
+ ot->description = "LANPR calculates feature line in current scene";
+ ot->idname = "SCENE_OT_lanpr_calculate";
+
+ ot->poll = lanpr_camera_exists;
+ ot->cancel = lanpr_compute_feature_lines_cancel;
+ ot->exec = lanpr_compute_feature_lines_exec;
+}
+
+/* Access */
+bool ED_lanpr_dpix_shader_error()
+{
+ return lanpr_share.dpix_shader_error;
+}
+
+/* Grease Pencil bindings */
+
+/* returns flags from LANPR_EdgeFlag */
+static int lanpr_object_line_types(Object *ob)
+{
+ ObjectLANPR *obl = &ob->lanpr;
+ int result = 0;
+ if (obl->contour.use) {
+ result |= LANPR_EDGE_FLAG_CONTOUR;
+ }
+ if (obl->crease.use) {
+ result |= LANPR_EDGE_FLAG_CREASE;
+ }
+ if (obl->material.use) {
+ result |= LANPR_EDGE_FLAG_MATERIAL;
+ }
+ if (obl->edge_mark.use) {
+ result |= LANPR_EDGE_FLAG_EDGE_MARK;
+ }
+ return result;
+}
+
+static void lanpr_generate_gpencil_from_chain(Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *UNUSED(gpl),
+ bGPDframe *gpf,
+ int level_start,
+ int level_end,
+ int material_nr,
+ Collection *col,
+ int types)
+{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ LANPR_RenderBuffer *rb = lanpr_share.render_buffer_shared;
+
+ if (rb == NULL) {
+ printf("NULL LANPR rb!\n");
+ return;
+ }
+ if (scene->lanpr.master_mode != LANPR_MASTER_MODE_SOFTWARE) {
+ return;
+ }
+
+ int color_idx = 0;
+ short thickness = 100;
+
+ float mat[4][4];
+
+ unit_m4(mat);
+
+ LANPR_RenderLineChain *rlc;
+ LANPR_RenderLineChainItem *rlci;
+ for (rlc = rb->chains.first; rlc; rlc = rlc->next) {
+
+ if (rlc->picked) {
+ continue;
+ }
+ if (ob && !rlc->object_ref) {
+ continue; /* intersection lines are all in the first collection running into here */
+ }
+ if (!(rlc->type & types)) {
+ continue;
+ }
+ if (rlc->level > level_end || rlc->level < level_start) {
+ continue;
+ }
+ if (ob && &ob->id != rlc->object_ref->id.orig_id) {
+ continue;
+ }
+ if (col && rlc->object_ref) {
+ if (!BKE_collection_has_object_recursive(col, (Object *)rlc->object_ref->id.orig_id)) {
+ continue;
+ }
+ }
+
+ rlc->picked = 1;
+
+ int array_idx = 0;
+ int count = ED_lanpr_count_chain(rlc);
+ bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false);
+
+ float *stroke_data = BLI_array_alloca(stroke_data, count * GP_PRIM_DATABUF_SIZE);
+
+ for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
+ float opatity = 1.0f; /* rlci->occlusion ? 0.0f : 1.0f; */
+ stroke_data[array_idx] = rlci->gpos[0];
+ stroke_data[array_idx + 1] = rlci->gpos[1];
+ stroke_data[array_idx + 2] = rlci->gpos[2];
+ stroke_data[array_idx + 3] = 1; /* thickness */
+ stroke_data[array_idx + 4] = opatity; /* hardness? */
+ array_idx += 5;
+ }
+
+ BKE_gpencil_stroke_add_points(gps, stroke_data, count, mat);
+ gps->mat_nr = material_nr;
+ }
+}
+static void lanpr_clear_gp_lanpr_flags(Depsgraph *dg, int frame)
+{
+ DEG_OBJECT_ITER_BEGIN (dg,
+ o,
+ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) {
+ if (o->type == OB_GPENCIL) {
+ bGPdata *gpd = ((Object *)o->id.orig_id)->data;
+ bGPDlayer *gpl;
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, frame);
+ if (gpf == NULL) {
+ continue;
+ }
+ gpf->flag &= ~GP_FRAME_LANPR_CLEARED;
+ }
+ }
+ }
+ DEG_OBJECT_ITER_END;
+}
+static void lanpr_update_gp_strokes_single(Depsgraph *dg,
+ Object *gpobj,
+ Object *ob,
+ int frame,
+ int level_start,
+ int level_end,
+ char *target_layer,
+ char *target_material,
+ Collection *col,
+ int type)
+{
+ bGPdata *gpd;
+ bGPDlayer *gpl;
+ bGPDframe *gpf;
+ gpd = gpobj->data;
+ gpl = BKE_gpencil_layer_get_by_name(gpd, target_layer, 1);
+ if (gpl == NULL) {
+ gpl = BKE_gpencil_layer_addnew(gpd, "lanpr_layer", true);
+ }
+ gpf = BKE_gpencil_layer_frame_get(gpl, frame, GP_GETFRAME_ADD_NEW);
+
+ if (gpf->strokes.first &&
+ !(DEG_get_evaluated_scene(dg)->lanpr.flags & LANPR_GPENCIL_OVERWRITE)) {
+ return;
+ }
+
+ if (!(gpf->flag & GP_FRAME_LANPR_CLEARED)) {
+ BKE_gpencil_free_strokes(gpf);
+ gpf->flag |= GP_FRAME_LANPR_CLEARED;
+ }
+
+ int use_material = BKE_gpencil_object_material_get_index_name(gpobj, target_material);
+ if (use_material < 0) {
+ use_material = 0;
+ }
+
+ lanpr_generate_gpencil_from_chain(
+ dg, ob, gpl, gpf, level_start, level_end, use_material, col, type);
+}
+static void lanpr_update_gp_strokes_recursive(
+ Depsgraph *dg, struct Collection *col, int frame, Object *source_only, Object *target_only)
+{
+ Object *ob;
+ Object *gpobj;
+ bGPdata *gpd;
+ CollectionObject *co;
+ CollectionChild *cc;
+
+ for (co = col->gobject.first; co || source_only; co = co->next) {
+ ob = source_only ? source_only : co->ob;
+
+ ObjectLANPR *obl = &ob->lanpr;
+ if (obl->target && obl->target->type == OB_GPENCIL) {
+ gpobj = obl->target;
+ gpd = gpobj->data;
+
+ if (target_only && target_only != gpobj) {
+ continue;
+ }
+
+ int level_start = obl->level_start;
+ int level_end = (obl->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS) ? obl->level_end :
+ obl->level_start;
+
+ if (obl->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ ob,
+ frame,
+ level_start,
+ level_end,
+ obl->target_layer,
+ obl->target_material,
+ NULL,
+ lanpr_object_line_types(ob));
+ }
+ else {
+ if (obl->contour.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ ob,
+ frame,
+ level_start,
+ level_end,
+ obl->contour.target_layer,
+ obl->contour.target_material,
+ NULL,
+ LANPR_EDGE_FLAG_CONTOUR);
+ }
+ if (obl->crease.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ ob,
+ frame,
+ level_start,
+ level_end,
+ obl->crease.target_layer,
+ obl->crease.target_material,
+ NULL,
+ LANPR_EDGE_FLAG_CREASE);
+ }
+ if (obl->material.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ ob,
+ frame,
+ level_start,
+ level_end,
+ obl->material.target_layer,
+ obl->material.target_material,
+ NULL,
+ LANPR_EDGE_FLAG_MATERIAL);
+ }
+ if (obl->edge_mark.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ ob,
+ frame,
+ level_start,
+ level_end,
+ obl->edge_mark.target_layer,
+ obl->edge_mark.target_material,
+ NULL,
+ LANPR_EDGE_FLAG_EDGE_MARK);
+ }
+ }
+
+ DEG_id_tag_update(&gpd->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ }
+ if (source_only) {
+ return;
+ }
+ }
+ for (cc = col->children.first; cc; cc = cc->next) {
+ lanpr_update_gp_strokes_recursive(dg, cc->collection, frame, source_only, target_only);
+ }
+}
+static int lanpr_collection_types(Collection *c)
+{
+ CollectionLANPR *cl = c->lanpr;
+ int result = 0;
+ if (cl->contour.use) {
+ result |= LANPR_EDGE_FLAG_CONTOUR;
+ }
+ if (cl->crease.use) {
+ result |= LANPR_EDGE_FLAG_CREASE;
+ }
+ if (cl->material.use) {
+ result |= LANPR_EDGE_FLAG_MATERIAL;
+ }
+ if (cl->edge_mark.use) {
+ result |= LANPR_EDGE_FLAG_EDGE_MARK;
+ }
+ if (cl->intersection.use) {
+ result |= LANPR_EDGE_FLAG_INTERSECTION;
+ }
+ return result;
+}
+static void lanpr_update_gp_strokes_collection(
+ Depsgraph *dg, struct Collection *col, int frame, int this_only, Object *target_only)
+{
+ Object *gpobj;
+ bGPdata *gpd = NULL;
+ CollectionChild *cc;
+
+ /* depth first */
+ if (!this_only) {
+ for (cc = col->children.first; cc; cc = cc->next) {
+ lanpr_update_gp_strokes_collection(dg, cc->collection, frame, this_only, target_only);
+ }
+ }
+
+ if (col->flag & COLLECTION_CONFIGURED_FOR_LANPR) {
+ if (col->lanpr->usage != COLLECTION_FEATURE_LINE_INCLUDE || !col->lanpr->target) {
+ return;
+ }
+ gpobj = col->lanpr->target;
+ if (target_only && target_only != gpobj) {
+ return;
+ }
+ CollectionLANPR *cl = col->lanpr;
+ int level_start = cl->level_start;
+ int level_end = (cl->flags & LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS) ? cl->level_end :
+ cl->level_start;
+
+ if (cl->flags & LANPR_LINE_LAYER_USE_SAME_STYLE) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ NULL,
+ frame,
+ level_start,
+ level_end,
+ cl->target_layer,
+ cl->target_material,
+ col,
+ lanpr_collection_types(col));
+ }
+ else {
+ if (cl->contour.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ NULL,
+ frame,
+ level_start,
+ level_end,
+ cl->contour.target_layer,
+ cl->contour.target_material,
+ col,
+ LANPR_EDGE_FLAG_CONTOUR);
+ }
+ if (cl->crease.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ NULL,
+ frame,
+ level_start,
+ level_end,
+ cl->crease.target_layer,
+ cl->crease.target_material,
+ col,
+ LANPR_EDGE_FLAG_CREASE);
+ }
+ if (cl->material.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ NULL,
+ frame,
+ level_start,
+ level_end,
+ cl->material.target_layer,
+ cl->material.target_material,
+ col,
+ LANPR_EDGE_FLAG_MATERIAL);
+ }
+ if (cl->edge_mark.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ NULL,
+ frame,
+ level_start,
+ level_end,
+ cl->edge_mark.target_layer,
+ cl->edge_mark.target_material,
+ col,
+ LANPR_EDGE_FLAG_EDGE_MARK);
+ }
+ if (cl->intersection.use) {
+ lanpr_update_gp_strokes_single(dg,
+ gpobj,
+ NULL,
+ frame,
+ level_start,
+ level_end,
+ cl->intersection.target_layer,
+ cl->intersection.target_material,
+ col,
+ LANPR_EDGE_FLAG_INTERSECTION);
+ }
+ }
+
+ if (gpd) {
+ DEG_id_tag_update(&gpd->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ }
+ }
+}
+static void lanpr_update_gp_strokes_actual(Scene *scene, Depsgraph *dg)
+{
+ int frame = scene->r.cfra;
+
+ ED_lanpr_compute_feature_lines_internal(dg, 0);
+
+ ED_lanpr_chain_clear_picked_flag(lanpr_share.render_buffer_shared);
+
+ lanpr_update_gp_strokes_recursive(dg, scene->master_collection, frame, NULL, NULL);
+
+ lanpr_update_gp_strokes_collection(dg, scene->master_collection, frame, 0, NULL);
+
+ lanpr_clear_gp_lanpr_flags(dg, frame);
+}
+static int lanpr_update_gp_strokes_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *dg = CTX_data_depsgraph_pointer(C);
+
+ lanpr_update_gp_strokes_actual(scene, dg);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL);
+
+ return OPERATOR_FINISHED;
+}
+static int lanpr_bake_gp_strokes_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *dg = CTX_data_depsgraph_pointer(C);
+ int frame;
+ int frame_begin = scene->r.sfra;
+ int frame_end = scene->r.efra;
+
+ for (frame = frame_begin; frame <= frame_end; frame++) {
+ // BKE_scene_frame_set(scene,frame);
+ DEG_evaluate_on_framechange(CTX_data_main(C), dg, frame);
+
+ ED_lanpr_compute_feature_lines_internal(dg, 0);
+
+ ED_lanpr_chain_clear_picked_flag(lanpr_share.render_buffer_shared);
+
+ lanpr_update_gp_strokes_recursive(dg, scene->master_collection, frame, NULL, NULL);
+
+ lanpr_update_gp_strokes_collection(dg, scene->master_collection, frame, 0, NULL);
+ }
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL);
+
+ return OPERATOR_FINISHED;
+}
+static int lanpr_update_gp_target_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *dg = CTX_data_depsgraph_pointer(C);
+ Object *gpo = CTX_data_active_object(C);
+
+ int frame = scene->r.cfra;
+
+ if (scene->lanpr.flags & LANPR_AUTO_UPDATE) {
+ ED_lanpr_compute_feature_lines_internal(dg, 0);
+ }
+
+ ED_lanpr_chain_clear_picked_flag(lanpr_share.render_buffer_shared);
+
+ lanpr_update_gp_strokes_recursive(dg, scene->master_collection, frame, NULL, gpo);
+
+ lanpr_update_gp_strokes_collection(dg, scene->master_collection, frame, 0, gpo);
+
+ lanpr_clear_gp_lanpr_flags(dg, frame);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL);
+
+ return OPERATOR_FINISHED;
+}
+static int lanpr_update_gp_source_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *dg = CTX_data_depsgraph_pointer(C);
+ Object *source_obj = CTX_data_active_object(C);
+
+ int frame = scene->r.cfra;
+
+ if (scene->lanpr.flags & LANPR_AUTO_UPDATE) {
+ ED_lanpr_compute_feature_lines_internal(dg, 0);
+ }
+
+ ED_lanpr_chain_clear_picked_flag(lanpr_share.render_buffer_shared);
+
+ lanpr_update_gp_strokes_recursive(dg, scene->master_collection, frame, source_obj, NULL);
+
+ lanpr_update_gp_strokes_collection(dg, scene->master_collection, frame, 0, NULL);
+
+ lanpr_clear_gp_lanpr_flags(dg, frame);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static bool lanpr_active_is_gpencil_object(bContext *C)
+{
+ Object *o = CTX_data_active_object(C);
+ return o->type == OB_GPENCIL;
+}
+static bool lanpr_active_is_source_object(bContext *C)
+{
+ Object *o = CTX_data_active_object(C);
+ if (o->type != OB_MESH) {
+ return false;
+ }
+ else {
+ if (o->lanpr.usage == OBJECT_FEATURE_LINE_INCLUDE) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void SCENE_OT_lanpr_update_gp_strokes(wmOperatorType *ot)
+{
+ ot->name = "Update LANPR Strokes";
+ ot->description = "Update strokes for LANPR grease pencil targets";
+ ot->idname = "SCENE_OT_lanpr_update_gp_strokes";
+
+ ot->exec = lanpr_update_gp_strokes_exec;
+}
+void SCENE_OT_lanpr_bake_gp_strokes(wmOperatorType *ot)
+{
+ ot->name = "Bake LANPR Strokes";
+ ot->description = "Bake strokes for LANPR grease pencil targets in all frames";
+ ot->idname = "SCENE_OT_lanpr_bake_gp_strokes";
+
+ ot->exec = lanpr_bake_gp_strokes_exec;
+}
+void OBJECT_OT_lanpr_update_gp_target(wmOperatorType *ot)
+{
+ ot->name = "Update Strokes";
+ ot->description = "Update LANPR strokes for selected GPencil object";
+ ot->idname = "OBJECT_OT_lanpr_update_gp_target";
+
+ ot->poll = lanpr_active_is_gpencil_object;
+ ot->exec = lanpr_update_gp_target_exec;
+}
+/* Not working due to lack of GP flags for the object */
+void OBJECT_OT_lanpr_update_gp_source(wmOperatorType *ot)
+{
+ ot->name = "Update Strokes";
+ ot->description = "Update LANPR strokes for selected Mesh object.";
+ ot->idname = "OBJECT_OT_lanpr_update_gp_source";
+
+ ot->poll = lanpr_active_is_source_object;
+ ot->exec = lanpr_update_gp_source_exec;
+}
+
+/* Post-frame updater */
+
+void ED_lanpr_post_frame_update_external(Scene *s, Depsgraph *dg)
+{
+ if ((s->lanpr.flags & LANPR_ENABLED) == 0 || !(s->lanpr.flags & LANPR_AUTO_UPDATE)) {
+ return;
+ }
+ if (strcmp(s->r.engine, RE_engine_id_BLENDER_LANPR)) {
+ /* Not LANPR engine, do GPencil updates. */
+ /* LANPR engine will automatically update when drawing the viewport. */
+ if (s->lanpr.flags & LANPR_AUTO_UPDATE) {
+ ED_lanpr_compute_feature_lines_internal(dg, 0);
+ lanpr_update_gp_strokes_actual(s, dg);
+ }
+ }
+}
diff --git a/source/blender/editors/lanpr/lanpr_intern.h b/source/blender/editors/lanpr/lanpr_intern.h
new file mode 100644
index 00000000000..c8a3feda915
--- /dev/null
+++ b/source/blender/editors/lanpr/lanpr_intern.h
@@ -0,0 +1,74 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#ifndef __LANPR_INTERN_H__
+#define __LANPR_INTERN_H__
+
+#include "BLI_listbase.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_threads.h"
+
+#include "DNA_lanpr_types.h"
+
+#include <math.h>
+#include <string.h>
+
+struct LANPR_StaticMemPoolNode;
+struct LANPR_RenderLine;
+struct LANPR_RenderBuffer;
+
+void *list_append_pointer_static(ListBase *h, struct LANPR_StaticMemPool *smp, void *p);
+void *list_append_pointer_static_sized(ListBase *h,
+ struct LANPR_StaticMemPool *smp,
+ void *p,
+ int size);
+void *list_push_pointer_static(ListBase *h, struct LANPR_StaticMemPool *smp, void *p);
+void *list_push_pointer_static_sized(ListBase *h,
+ struct LANPR_StaticMemPool *smp,
+ void *p,
+ int size);
+
+void *list_append_pointer_static_pool(struct LANPR_StaticMemPool *mph, ListBase *h, void *p);
+void *list_pop_pointer_no_free(ListBase *h);
+void list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
+
+LANPR_StaticMemPoolNode *mem_new_static_pool(struct LANPR_StaticMemPool *smp);
+void *mem_static_aquire(struct LANPR_StaticMemPool *smp, int size);
+void *mem_static_aquire_thread(struct LANPR_StaticMemPool *smp, int size);
+void *mem_static_destroy(LANPR_StaticMemPool *smp);
+
+void tmat_make_ortho_matrix_44d(double (*mProjection)[4],
+ double xMin,
+ double xMax,
+ double yMin,
+ double yMax,
+ double zMin,
+ double zMax);
+void tmat_make_perspective_matrix_44d(
+ double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax);
+
+int lanpr_count_this_line(struct LANPR_RenderLine *rl, struct LANPR_LineLayer *ll);
+int lanpr_count_intersection_segment_count(struct LANPR_RenderBuffer *rb);
+
+#endif
diff --git a/source/blender/editors/lanpr/lanpr_ops.c b/source/blender/editors/lanpr/lanpr_ops.c
new file mode 100644
index 00000000000..57fddee25aa
--- /dev/null
+++ b/source/blender/editors/lanpr/lanpr_ops.c
@@ -0,0 +1,48 @@
+/*
+ * 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 "BLI_utildefines.h"
+
+#include "WM_api.h"
+
+#include "ED_lanpr.h"
+
+void ED_operatortypes_lanpr(void)
+{
+ /* lanpr: */
+ WM_operatortype_append(SCENE_OT_lanpr_calculate_feature_lines);
+ WM_operatortype_append(SCENE_OT_lanpr_add_line_layer);
+ WM_operatortype_append(SCENE_OT_lanpr_delete_line_layer);
+ WM_operatortype_append(SCENE_OT_lanpr_rebuild_all_commands);
+ WM_operatortype_append(SCENE_OT_lanpr_auto_create_line_layer);
+ WM_operatortype_append(SCENE_OT_lanpr_move_line_layer);
+ WM_operatortype_append(SCENE_OT_lanpr_enable_all_line_types);
+ WM_operatortype_append(SCENE_OT_lanpr_update_gp_strokes);
+ WM_operatortype_append(SCENE_OT_lanpr_bake_gp_strokes);
+
+ WM_operatortype_append(OBJECT_OT_lanpr_update_gp_target);
+ /* Not working */
+ /* WM_operatortype_append(OBJECT_OT_lanpr_update_gp_source); */
+}
diff --git a/source/blender/editors/lanpr/lanpr_util.c b/source/blender/editors/lanpr/lanpr_util.c
new file mode 100644
index 00000000000..6fd072bdce2
--- /dev/null
+++ b/source/blender/editors/lanpr/lanpr_util.c
@@ -0,0 +1,191 @@
+/*
+ * 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 <stdio.h>
+/* #include <time.h> */
+#include "ED_lanpr.h"
+#include "MEM_guardedalloc.h"
+#include <math.h>
+
+#include "lanpr_intern.h"
+
+/* ===================================================================[slt] */
+
+void *list_append_pointer_static(ListBase *h, LANPR_StaticMemPool *smp, void *data)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = mem_static_aquire(smp, sizeof(LinkData));
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+void *list_append_pointer_static_sized(ListBase *h, LANPR_StaticMemPool *smp, void *data, int size)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = mem_static_aquire(smp, size);
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+
+void *list_append_pointer_static_pool(LANPR_StaticMemPool *mph, ListBase *h, void *data)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = mem_static_aquire(mph, sizeof(LinkData));
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+void *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 list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
+{
+ BLI_remlink(h, (void *)lip);
+}
+
+LANPR_StaticMemPoolNode *mem_new_static_pool(LANPR_StaticMemPool *smp)
+{
+ LANPR_StaticMemPoolNode *smpn = MEM_callocN(LANPR_MEMORY_POOL_128MB, "mempool");
+ smpn->used_byte = sizeof(LANPR_StaticMemPoolNode);
+ BLI_addhead(&smp->pools, smpn);
+ return smpn;
+}
+void *mem_static_aquire(LANPR_StaticMemPool *smp, int size)
+{
+ LANPR_StaticMemPoolNode *smpn = smp->pools.first;
+ void *ret;
+
+ if (!smpn || (smpn->used_byte + size) > LANPR_MEMORY_POOL_128MB) {
+ smpn = mem_new_static_pool(smp);
+ }
+
+ ret = ((unsigned char *)smpn) + smpn->used_byte;
+
+ smpn->used_byte += size;
+
+ return ret;
+}
+void *mem_static_aquire_thread(LANPR_StaticMemPool *smp, int size)
+{
+ LANPR_StaticMemPoolNode *smpn = smp->pools.first;
+ void *ret;
+
+ BLI_spin_lock(&smp->lock_mem);
+
+ if (!smpn || (smpn->used_byte + size) > LANPR_MEMORY_POOL_128MB) {
+ smpn = mem_new_static_pool(smp);
+ }
+
+ ret = ((unsigned char *)smpn) + smpn->used_byte;
+
+ smpn->used_byte += size;
+
+ BLI_spin_unlock(&smp->lock_mem);
+
+ return ret;
+}
+void *mem_static_destroy(LANPR_StaticMemPool *smp)
+{
+ LANPR_StaticMemPoolNode *smpn;
+ void *ret = 0;
+
+ while ((smpn = BLI_pophead(&smp->pools)) != NULL) {
+ MEM_freeN(smpn);
+ }
+
+ smp->each_size = 0;
+
+ return ret;
+}
+
+/* =======================================================================[str] */
+
+void tmat_make_perspective_matrix_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 tmat_make_ortho_matrix_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;
+}
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index e41445aef09..e153140e253 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -89,6 +89,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_BULLET)
add_definitions(-DWITH_BULLET)
endif()
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 684bb73dc0e..1096c5836d1 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -7676,7 +7676,7 @@ void MESH_OT_symmetry_snap(struct wmOperatorType *ot)
/** \} */
-#ifdef WITH_FREESTYLE
+#if (defined(WITH_FREESTYLE) || defined(WITH_LANPR))
/* -------------------------------------------------------------------- */
/** \name Mark Edge (Freestyle) Operator
@@ -7836,7 +7836,7 @@ void MESH_OT_mark_freestyle_face(wmOperatorType *ot)
/** \} */
-#endif /* WITH_FREESTYLE */
+#endif /* WITH_FREESTYLE || WITH_LANPR */
/* -------------------------------------------------------------------- */
/** \name Loop Normals Editing Tools Modal Map
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 5e70069456b..c9f841a059f 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -257,7 +257,7 @@ void MESH_OT_paint_mask_slice(struct wmOperatorType *ot);
struct wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf);
-#ifdef WITH_FREESTYLE
+#if (defined(WITH_FREESTYLE) || defined(WITH_LANPR))
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 c52a5956ac4..adaf146d5b9 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -133,7 +133,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_loop_multi_select);
WM_operatortype_append(MESH_OT_mark_seam);
WM_operatortype_append(MESH_OT_mark_sharp);
-#ifdef WITH_FREESTYLE
+#if (defined(WITH_FREESTYLE) || defined(WITH_LANPR))
WM_operatortype_append(MESH_OT_mark_freestyle_edge);
#endif
WM_operatortype_append(MESH_OT_vertices_smooth);
diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt
index 7f7748bf52f..6a54c465b33 100644
--- a/source/blender/editors/render/CMakeLists.txt
+++ b/source/blender/editors/render/CMakeLists.txt
@@ -70,6 +70,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 10f69f3fe9d..b4e5eb2a047 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -59,6 +59,10 @@
#include "ED_render.h"
#include "ED_view3d.h"
+#ifdef WITH_LANPR
+# include "ED_lanpr.h"
+#endif
+
#include "DEG_depsgraph.h"
#include "WM_api.h"
@@ -95,6 +99,10 @@ void ED_render_scene_update(const DEGEditorUpdateContext *update_ctx, int update
return;
}
+#ifdef WITH_LANPR
+ /* Temporary solution for updating LANPR GPencil targets. */
+ ED_lanpr_post_frame_update_external(scene, update_ctx->depsgraph);
+#endif
recursive_check = true;
C = CTX_create();
diff --git a/source/blender/editors/space_api/CMakeLists.txt b/source/blender/editors/space_api/CMakeLists.txt
index de1c905b08b..cd66cd8a6d4 100644
--- a/source/blender/editors/space_api/CMakeLists.txt
+++ b/source/blender/editors/space_api/CMakeLists.txt
@@ -55,4 +55,8 @@ set(LIB
bf_editor_space_view3d
)
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
blender_add_lib(bf_editor_space_api "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index e084d5fcdf2..9b752fbebe9 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -64,6 +64,10 @@
#include "ED_userpref.h"
#include "ED_uvedit.h"
+#ifdef WITH_LANPR
+# include "ED_lanpr.h"
+#endif
+
#include "io_ops.h"
/* only call once on startup, storage is global in BKE kernel listbase */
@@ -123,6 +127,10 @@ void ED_spacetypes_init(void)
ED_operatortypes_view2d();
ED_operatortypes_ui();
+#ifdef WITH_LANPR
+ ED_operatortypes_lanpr();
+#endif
+
ED_screen_user_menu_register();
/* gizmo types */
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index c3b7d65689f..41eab7a9b6d 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -54,6 +54,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_NEW_OBJECT_TYPES)
add_definitions(-DWITH_NEW_OBJECT_TYPES)
endif()
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 7e6088bc3cc..2eab9eddf10 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -164,6 +164,29 @@ static int buttons_context_path_world(ButsContextPath *path)
return 0;
}
+static int 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 1;
+ }
+ /* if we have a view layer, use the view layer's active collection */
+ else if (buttons_context_path_view_layer(path, window)) {
+ ViewLayer *view_layer = path->ptr[path->len - 1].data;
+ Collection *c = view_layer->active_collection->collection;
+ if (c) {
+ RNA_id_pointer_create(&c->id, &path->ptr[path->len]);
+ path->len++;
+ return 1;
+ }
+ }
+
+ /* no path to a collection possible */
+ return 0;
+}
+
static int buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window)
{
FreestyleLineStyle *linestyle;
@@ -595,6 +618,26 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma
case BCONTEXT_WORLD:
found = buttons_context_path_world(path);
break;
+ case BCONTEXT_COLLECTION: /* This is for LANPR collection flags */
+#ifdef WITH_LANPR
+ found = buttons_context_path_collection(path, window);
+#else
+ BLI_assert(!"'WITH_LANPR' disabled - should not possible to access 'BCONTEXT_COLLECTION'");
+#endif
+ break;
+ case BCONTEXT_LANPR: /* This is for LANPR object flags */
+#ifdef WITH_LANPR
+ if (scene &&
+ ((scene->lanpr.flags & LANPR_ENABLED) || !strcmp(scene->r.engine, "BLENDER_LANPR"))) {
+ found = buttons_context_path_object(path);
+ }
+ else {
+ found = 0;
+ }
+#else
+ BLI_assert(!"'WITH_LANPR' disabled - should not possible to access 'BCONTEXT_LANPR'");
+#endif
+ break;
case BCONTEXT_TOOL:
found = true;
break;
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index c06c107d4a3..a91f3e60fac 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -160,6 +160,21 @@ static void buttons_main_region_layout_properties(const bContext *C,
case BCONTEXT_WORLD:
contexts[0] = "world";
break;
+
+ case BCONTEXT_COLLECTION:
+#ifdef WITH_LANPR
+ contexts[0] = "collection";
+#else
+ BLI_assert(!"'WITH_LANPR' disabled - should not possible to access 'BCONTEXT_COLLECTION'");
+#endif
+ break;
+ case BCONTEXT_LANPR:
+#ifdef WITH_LANPR
+ contexts[0] = "lanpr";
+#else
+ BLI_assert(!"'WITH_LANPR' disabled - should not possible to access 'BCONTEXT_COLLECTION'");
+#endif
+ break;
case BCONTEXT_OBJECT:
contexts[0] = "object";
break;
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index f2536cfac62..9ae501dc060 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -95,6 +95,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_XR_OPENXR)
add_definitions(-DWITH_XR_OPENXR)
endif()
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 4fc98789c18..74c6692337e 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1058,6 +1058,11 @@ static void view3d_main_region_message_subscribe(const struct bContext *C,
}
WM_msg_subscribe_rna_anon_type(mbus, SceneEEVEE, &msg_sub_value_region_tag_redraw);
+
+#ifdef WITH_LANPR
+ WM_msg_subscribe_rna_anon_type(mbus, SceneLANPR, &msg_sub_value_region_tag_redraw);
+#endif
+
WM_msg_subscribe_rna_anon_type(mbus, SceneDisplay, &msg_sub_value_region_tag_redraw);
WM_msg_subscribe_rna_anon_type(mbus, ObjectDisplay, &msg_sub_value_region_tag_redraw);
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 17a90d10ca7..54e472ef301 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SRC
../include/ED_keyframes_draw.h
../include/ED_keyframes_edit.h
../include/ED_keyframing.h
+ ../include/ED_lanpr.h
../include/ED_lattice.h
../include/ED_logic.h
../include/ED_markers.h
@@ -112,6 +113,10 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
list(APPEND INC
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 2e449f10563..a747ed3be11 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -351,14 +351,24 @@ typedef enum eGp_Vertex_Mode {
GPPAINT_MODE_BOTH = 2,
} eGp_Vertex_Mode;
-/* sculpt_flag */
+/* GP_Sculpt_Data.flag */
typedef enum eGP_Sculpt_Flag {
/* invert the effect of the brush */
GP_SCULPT_FLAG_INVERT = (1 << 0),
+ /* adjust strength using pen pressure */
+ GP_SCULPT_FLAG_USE_PRESSURE = (1 << 1),
+
+ /* strength of brush falls off with distance from cursor */
+ GP_SCULPT_FLAG_USE_FALLOFF = (1 << 2),
+
/* smooth brush affects pressure values as well */
- GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2),
+ GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 3),
+ /* enable screen cursor */
+ GP_SCULPT_FLAG_ENABLE_CURSOR = (1 << 4),
/* temporary invert action */
- GP_SCULPT_FLAG_TMP_INVERT = (1 << 3),
+ GP_SCULPT_FLAG_TMP_INVERT = (1 << 5),
+ /* adjust radius using pen pressure */
+ GP_SCULPT_FLAG_PRESSURE_RADIUS = (1 << 6),
} eGP_Sculpt_Flag;
/* sculpt_mode_flag */
diff --git a/source/blender/makesdna/DNA_collection_types.h b/source/blender/makesdna/DNA_collection_types.h
index 13eb8a762d9..75b77b09f7d 100644
--- a/source/blender/makesdna/DNA_collection_types.h
+++ b/source/blender/makesdna/DNA_collection_types.h
@@ -28,6 +28,7 @@
#include "DNA_ID.h"
#include "DNA_defs.h"
+#include "DNA_lanpr_types.h"
#include "DNA_listBase.h"
struct Collection;
@@ -43,6 +44,39 @@ typedef struct CollectionChild {
struct Collection *collection;
} CollectionChild;
+enum CollectionFeatureLine_Usage {
+ COLLECTION_FEATURE_LINE_INCLUDE = 0,
+ COLLECTION_FEATURE_LINE_OCCLUSION_ONLY = (1 << 0),
+ COLLECTION_FEATURE_LINE_EXCLUDE = (1 << 1),
+};
+
+typedef struct CollectionLANPRLineType {
+ int use;
+ char _pad[4];
+ char target_layer[128];
+ char target_material[128];
+} CollectionLANPRLineType;
+
+typedef struct CollectionLANPR {
+ int usage;
+
+ /* Separate flags for LANPR shared flag values. */
+ int flags;
+
+ struct Object *target;
+ char target_layer[128];
+ char target_material[128];
+
+ struct CollectionLANPRLineType contour;
+ struct CollectionLANPRLineType crease;
+ struct CollectionLANPRLineType material;
+ struct CollectionLANPRLineType edge_mark;
+ struct CollectionLANPRLineType intersection;
+
+ int level_start;
+ int level_end;
+} CollectionLANPR;
+
typedef struct Collection {
ID id;
@@ -61,6 +95,9 @@ typedef struct Collection {
short tag;
char _pad[4];
+ /** LANPR engine specific */
+ CollectionLANPR *lanpr;
+
/* Runtime. Cache of objects in this collection and all its
* children. This is created on demand when e.g. some physics
* simulation needs it, we don't want to have it for every
@@ -78,12 +115,13 @@ typedef struct Collection {
/* Collection->flag */
enum {
- COLLECTION_RESTRICT_VIEWPORT = (1 << 0), /* Disable in viewports. */
- COLLECTION_RESTRICT_SELECT = (1 << 1), /* Not selectable in viewport. */
- /* COLLECTION_DISABLED_DEPRECATED = (1 << 2), */ /* Not used anymore */
- 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_RESTRICT_VIEWPORT = (1 << 0), /* Disable in viewports. */
+ COLLECTION_RESTRICT_SELECT = (1 << 1), /* Not selectable in viewport. */
+ COLLECTION_DISABLED_DEPRECATED = (1 << 2), /* Not used anymore */
+ 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_CONFIGURED_FOR_LANPR = (1 << 6), /* Configurations saved to collection->lanpr. */
};
/* Collection->tag */
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index c3180ae79db..4558778f608 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -335,6 +335,8 @@ typedef enum eGPDframe_Flag {
GP_FRAME_PAINT = (1 << 0),
/* for editing in Action Editor */
GP_FRAME_SELECT = (1 << 1),
+ /* LANPR generation */
+ GP_FRAME_LANPR_CLEARED = (1 << 2),
} eGPDframe_Flag;
/* ***************************************** */
diff --git a/source/blender/makesdna/DNA_lanpr_types.h b/source/blender/makesdna/DNA_lanpr_types.h
new file mode 100644
index 00000000000..3bd4291667e
--- /dev/null
+++ b/source/blender/makesdna/DNA_lanpr_types.h
@@ -0,0 +1,126 @@
+/*
+ * ***** 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_LANPR_TYPES_H__
+#define __DNA_LANPR_TYPES_H__
+
+/** \file DNA_lanpr_types.h
+ * \ingroup DNA
+ */
+
+#include "DNA_listBase.h"
+#include "DNA_ID.h"
+#include "DNA_collection_types.h"
+
+struct Object;
+struct Material;
+struct Collection;
+
+typedef enum LANPR_TaperSettings {
+ LANPR_USE_DIFFERENT_TAPER = 0,
+ LANPR_USE_SAME_TAPER = 1,
+} LANPR_TaperSettings;
+
+typedef enum LANPR_NomalEffect {
+ /* Shouldn't have access to zero value. */
+ /* Enable/disable is another flag. */
+ LANPR_NORMAL_DIRECTIONAL = 1,
+ LANPR_NORMAL_POINT = 2,
+} LANPR_NomalEffect;
+
+typedef enum LANPR_ComponentMode {
+ LANPR_COMPONENT_MODE_ALL = 0,
+ LANPR_COMPONENT_MODE_OBJECT = 1,
+ LANPR_COMPONENT_MODE_MATERIAL = 2,
+ LANPR_COMPONENT_MODE_COLLECTION = 3,
+} LANPR_ComponentMode;
+
+typedef enum LANPR_ComponentUsage {
+ LANPR_COMPONENT_INCLUSIVE = 0,
+ LANPR_COMPONENT_EXCLUSIVE = 1,
+} LANPR_ComponentUsage;
+
+typedef enum LANPR_ComponentLogic {
+ LANPR_COMPONENT_LOGIG_OR = 0,
+ LANPR_COMPONENT_LOGIC_AND = 1,
+} LANPR_ComponentLogic;
+
+struct DRWShadingGroup;
+
+typedef struct LANPR_LineType {
+ int use;
+ float thickness;
+ float color[4];
+} LANPR_LineType;
+
+typedef enum LANPR_LineLayerFlags {
+ LANPR_LINE_LAYER_USE_SAME_STYLE = (1 << 0), /* Share with object lanpr flags */
+ LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS = (1 << 1), /* Share with object lanpr flags */
+ LANPR_LINE_LAYER_NORMAL_ENABLED = (1 << 2),
+ LANPR_LINE_LAYER_NORMAL_INVERSE = (1 << 3),
+ LANPR_LINE_LAYER_REPLACE_STROKES = (1 << 4),
+ LANPR_LINE_LAYER_COLLECTION_FORCE = (1 << 5),
+} LANPR_LineLayerFlags;
+
+typedef struct LANPR_LineLayer {
+ struct LANPR_LineLayer *next, *prev;
+
+ int flags;
+ int _pad1;
+ int level_start;
+ int level_end;
+
+ /** To be displayed on the list */
+ char name[64];
+
+ LANPR_LineType contour;
+ LANPR_LineType crease;
+ LANPR_LineType edge_mark;
+ LANPR_LineType material_separate;
+ LANPR_LineType intersection;
+
+ float thickness;
+
+ float color[4];
+
+ int normal_mode;
+ float normal_ramp_begin;
+ float normal_ramp_end;
+ float normal_thickness_start;
+ float normal_thickness_end;
+ struct Object *normal_control_object;
+
+ /** For component evaluation */
+ int logic_mode;
+ int _pad3;
+
+ struct DRWShadingGroup *shgrp;
+ struct GPUBatch *batch;
+
+} LANPR_LineLayer;
+
+#endif
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 47e6db835c9..8ba8c297e70 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -27,6 +27,11 @@
#include "DNA_object_enums.h"
+#include "DNA_defs.h"
+#include "DNA_customdata_types.h"
+#include "DNA_lanpr_types.h"
+#include "DNA_listBase.h"
+
#include "DNA_ID.h"
#include "DNA_action_types.h" /* bAnimVizSettings */
#include "DNA_customdata_types.h"
@@ -191,6 +196,40 @@ typedef struct Object_Runtime {
short _pad2[3];
} Object_Runtime;
+typedef struct ObjectLANPRLineType {
+ int use;
+ char _pad[4];
+ char target_layer[128];
+ char target_material[128];
+} ObjectLANPRLineType;
+
+typedef struct ObjectLANPR {
+ int usage;
+
+ /* Separate flags for LANPR shared flag values. */
+ int flags;
+
+ struct Object *target;
+ char target_layer[128];
+ char target_material[128];
+
+ ObjectLANPRLineType crease;
+ ObjectLANPRLineType contour;
+ ObjectLANPRLineType material;
+ ObjectLANPRLineType edge_mark;
+ /* Intersection not implemented as per-object */
+
+ int level_start;
+ int level_end;
+} ObjectLANPR;
+
+enum ObjectFeatureLine_Usage {
+ OBJECT_FEATURE_LINE_INHERENT = 0,
+ OBJECT_FEATURE_LINE_INCLUDE = (1 << 0),
+ OBJECT_FEATURE_LINE_OCCLUSION_ONLY = (1 << 1),
+ OBJECT_FEATURE_LINE_EXCLUDE = (1 << 2),
+};
+
typedef struct Object {
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
@@ -400,6 +439,8 @@ typedef struct Object {
struct PreviewImage *preview;
+ ObjectLANPR lanpr;
+
/** Runtime evaluation data (keep last). */
Object_Runtime runtime;
} Object;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 8c34a7cb0a1..18cd3872d1f 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -29,8 +29,10 @@
/* XXX, temp feature - campbell */
#define DURIAN_CAMERA_SWITCH
-#include "DNA_ID.h"
-#include "DNA_collection_types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "DNA_color_types.h" /* color management */
#include "DNA_curveprofile_types.h"
#include "DNA_customdata_types.h" /* Scene's runtime cddata masks. */
@@ -42,10 +44,6 @@
#include "DNA_vec_types.h"
#include "DNA_view3d_types.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
struct AnimData;
struct Brush;
struct Collection;
@@ -1001,7 +999,8 @@ typedef struct UvSculpt {
/* grease pencil drawing brushes */
typedef struct GpPaint {
Paint paint;
- int flag;
+ int flag; /* LANPR stuff */
+ /*struct SceneLANPR lanpr; XXX: Why this ended up here? */
/* Mode of paint (Materials or Vertex Color). */
int mode;
} GpPaint;
@@ -1063,6 +1062,27 @@ typedef enum eGP_Lockaxis_Types {
GP_LOCKAXIS_CURSOR = 4,
} eGP_Lockaxis_Types;
+/* GP_Sculpt_Settings.brushtype */
+typedef enum eGP_Sculpt_Types {
+ GP_SCULPT_TYPE_SMOOTH = 0,
+ GP_SCULPT_TYPE_THICKNESS = 1,
+ GP_SCULPT_TYPE_STRENGTH = 2,
+ GP_SCULPT_TYPE_GRAB = 3,
+ GP_SCULPT_TYPE_PUSH = 4,
+ GP_SCULPT_TYPE_TWIST = 5,
+ GP_SCULPT_TYPE_PINCH = 6,
+ GP_SCULPT_TYPE_RANDOMIZE = 7,
+ GP_SCULPT_TYPE_CLONE = 8,
+ GP_SCULPT_TYPE_SUBDIVIDE = 9,
+ GP_SCULPT_TYPE_SIMPLIFY = 10,
+ /* add any sculpt brush above this value */
+ GP_SCULPT_TYPE_WEIGHT = 11,
+ /* add any weight paint brush below this value. Do no mix brushes */
+
+ /* !!! Update GP_Sculpt_Data brush[###]; below !!! */
+ GP_SCULPT_TYPE_MAX,
+} eGP_Sculpt_Types;
+
/* Settings for a GPencil Speed Guide */
typedef struct GP_Sculpt_Guide {
char use_guide;
@@ -1077,17 +1097,43 @@ typedef struct GP_Sculpt_Guide {
struct Object *reference_object;
} GP_Sculpt_Guide;
-/* GPencil Stroke Sculpting Settings */
-typedef struct GP_Sculpt_Settings {
+typedef struct GP_Sculpt_Data {
+ /** Radius of brush. */
+ short size;
+ /** EGP_Sculpt_Flag. */
+ short flag;
+ /** Strength of effect. */
+ float strength;
+ /** Cursor color for add. */
+ float curcolor_add[3];
+ /** Cursor color for sub. */
+ float curcolor_sub[3];
+ /** Target weight. */
+ float weight;
+ char _pad[4];
+} GP_Sculpt_Data;
+
+/* GPencil Stroke Sculpting Settings */ typedef struct GP_Sculpt_Settings {
+ /** GP_SCULPT_TYPE_MAX. */
+ GP_Sculpt_Data brush[12];
/** Runtime. */
void *paintcursor;
+
+ /** #eGP_Sculpt_Types (sculpt). */
+ int brushtype;
/** #eGP_Sculpt_SettingsFlag. */
int flag;
/** #eGP_Lockaxis_Types lock drawing to one axis. */
int lock_axis;
/** Threshold for intersections */
float isect_threshold;
- char _pad_[4];
+
+ /* weight paint is a submode of sculpt but use its own index. All weight paint
+ * brushes must be defined at the end of the brush array.
+ */
+ /** #eGP_Sculpt_Types (weight paint). */
+ int weighttype;
+ char _pad[4];
/** Multiframe edit falloff effect by frame. */
struct CurveMapping *cur_falloff;
/** Curve used for primitive tools. */
@@ -1645,6 +1691,76 @@ typedef struct SceneEEVEE {
float light_threshold;
} SceneEEVEE;
+/* LANPR Global Config */
+
+struct LANPR_RenderBuffer;
+struct LANPR_LineLayer;
+
+typedef enum LANPR_MasterMode {
+ LANPR_MASTER_MODE_SOFTWARE = 0,
+ LANPR_MASTER_MODE_DPIX = 1,
+} LANPR_MasterMode;
+
+typedef enum LANPR_PostProcessingStatus {
+ LANPR_POST_PROCESSING_DISABLED = 0,
+ LANPR_POST_PROCESSING_ENABLED = 1,
+} LANPR_PostProcessingStatus;
+
+typedef enum LANPR_MainFlags {
+ LANPR_ENABLED = (1 << 0),
+ /* For LANPR->GP and viewport to update automatically. */
+ LANPR_AUTO_UPDATE = (1 << 1),
+ LANPR_SAME_TAPER = (1 << 2),
+ /* Edge split modifier will cause problems in LANPR. */
+ LANPR_DISABLE_EDGE_SPLITS = (1 << 3),
+ LANPR_USE_CHAINING = (1 << 4),
+ LANPR_USE_INTERSECTIONS = (1 << 5),
+ /* Overwrite existing strokes in this frame. */
+ LANPR_GPENCIL_OVERWRITE = (1 << 6),
+} LANPR_MainFlags;
+
+typedef struct SceneLANPR {
+
+ int flags;
+
+ int master_mode; /* LANPR_MasterMode */
+
+ float taper_left_distance;
+ float taper_left_strength;
+ float taper_right_distance;
+ float taper_right_strength;
+
+ /* shared */
+
+ float contour_fade; /* for dpix contour fading,reserved for future usage */
+ float crease_threshold; /* 0-1 range for cosine angle */
+ float crease_fade_threshold; /* for dpix crease fading */
+
+ float line_color[4];
+
+ float depth_width_influence;
+ float depth_width_curve;
+ float depth_alpha_influence;
+ float depth_alpha_curve;
+
+ int gpu_cache_size; /* enum! */
+
+ /* CPU mode */
+ ListBase line_layers;
+ struct LANPR_LineLayer *active_layer;
+ float chaining_geometry_threshold;
+ float chaining_image_threshold;
+} SceneLANPR;
+
+enum {
+ LANPR_GPU_CACHE_SIZE_512 = 0,
+ LANPR_GPU_CACHE_SIZE_1K = 1, /* default */
+ LANPR_GPU_CACHE_SIZE_2K = 2,
+ LANPR_GPU_CACHE_SIZE_4K = 3,
+ LANPR_GPU_CACHE_SIZE_8K = 4,
+ LANPR_GPU_CACHE_SIZE_16K = 5,
+};
+
typedef struct SceneGpencil {
float smaa_threshold;
char _pad[4];
@@ -1781,7 +1897,11 @@ typedef struct Scene {
struct SceneDisplay display;
struct SceneEEVEE eevee;
+
struct SceneGpencil grease_pencil_settings;
+
+ /* LANPR stuff */
+ struct SceneLANPR lanpr;
} Scene;
/* **************** RENDERDATA ********************* */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 31a8d967ccf..d3d86216843 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -211,6 +211,8 @@ typedef enum eSpaceButtons_Context {
BCONTEXT_TOOL = 14,
BCONTEXT_SHADERFX = 15,
BCONTEXT_OUTPUT = 16,
+ BCONTEXT_COLLECTION = 17,
+ BCONTEXT_LANPR = 18,
/* Keep last. */
BCONTEXT_TOT,
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 09cfe54e94d..d253685dbfd 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_lanpr_types.h",
"DNA_lattice_types.h",
"DNA_object_types.h",
"DNA_object_force_types.h",
@@ -1575,6 +1576,10 @@ int main(int argc, char **argv)
#include "DNA_meshdata_types.h"
#include "DNA_meta_types.h"
#include "DNA_modifier_types.h"
+#include "DNA_lanpr_types.h"
+#include "DNA_lattice_types.h"
+#include "DNA_object_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_nla_types.h"
#include "DNA_node_types.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 65c43ebc151..709b47e73c3 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -106,6 +106,7 @@ extern StructRNA RNA_ClothSettings;
extern StructRNA RNA_CloudsTexture;
extern StructRNA RNA_Collection;
extern StructRNA RNA_CollectionProperty;
+extern StructRNA RNA_CollectionLANPR;
extern StructRNA RNA_CollisionModifier;
extern StructRNA RNA_CollisionSettings;
extern StructRNA RNA_ColorGpencilModifier;
@@ -431,6 +432,7 @@ extern StructRNA RNA_NormalEditModifier;
extern StructRNA RNA_Object;
extern StructRNA RNA_ObjectBase;
extern StructRNA RNA_ObjectDisplay;
+extern StructRNA RNA_ObjectLANPR;
extern StructRNA RNA_OceanModifier;
extern StructRNA RNA_OceanTexData;
extern StructRNA RNA_OffsetGpencilModifier;
@@ -491,6 +493,7 @@ extern StructRNA RNA_SPHFluidSettings;
extern StructRNA RNA_Scene;
extern StructRNA RNA_SceneDisplay;
extern StructRNA RNA_SceneEEVEE;
+extern StructRNA RNA_SceneLANPR;
extern StructRNA RNA_SceneObjects;
extern StructRNA RNA_SceneRenderLayer;
extern StructRNA RNA_SceneSequence;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 024bdbe5ed5..a83c8697329 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -54,6 +54,7 @@ set(DEFSRC
rna_light.c
rna_lightprobe.c
rna_linestyle.c
+ rna_lanpr.c
rna_main.c
rna_mask.c
rna_material.c
@@ -322,6 +323,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_OPENSUBDIV)
list(APPEND INC
../../../../intern/opensubdiv
@@ -440,6 +445,12 @@ set(LIB
bf_editor_undo
)
+if(WITH_LANPR)
+ list(APPEND LIB
+ bf_editor_lanpr
+ )
+endif()
+
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_rna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 9153130546e..85bd17c737e 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4278,6 +4278,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_lattice.c", "rna_lattice_api.c", RNA_def_lattice},
{"rna_layer.c", NULL, RNA_def_view_layer},
{"rna_linestyle.c", NULL, RNA_def_linestyle},
+ {"rna_lanpr.c", NULL, RNA_def_lanpr},
{"rna_main.c", "rna_main_api.c", RNA_def_main},
{"rna_fluid.c", NULL, RNA_def_fluid},
{"rna_material.c", "rna_material_api.c", RNA_def_material},
diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c
index fe64ead3f89..18468323cfe 100644
--- a/source/blender/makesrna/intern/rna_collection.c
+++ b/source/blender/makesrna/intern/rna_collection.c
@@ -22,8 +22,12 @@
#include "DNA_collection_types.h"
+#include "DNA_lanpr_types.h"
+
#include "BLI_utildefines.h"
+#include "MEM_guardedalloc.h"
+
#include "RNA_define.h"
#include "rna_internal.h"
@@ -326,6 +330,31 @@ static void rna_Collection_flag_update(Main *bmain, Scene *scene, PointerRNA *pt
WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene);
}
+/* LANPR */
+
+static bool rna_Collection_lanpr_configure_get(PointerRNA *ptr)
+{
+ Collection *c = (Collection *)ptr->owner_id;
+ return (c->flag & COLLECTION_CONFIGURED_FOR_LANPR);
+}
+static void rna_Collection_lanpr_configure_set(PointerRNA *ptr, const bool value)
+{
+ Collection *c = (Collection *)ptr->owner_id;
+ CollectionLANPR *lanpr = c->lanpr;
+
+ if (value) {
+ c->flag |= COLLECTION_CONFIGURED_FOR_LANPR;
+ if (!lanpr) {
+ lanpr = MEM_callocN(sizeof(CollectionLANPR), "CollectionLANPR");
+ c->lanpr = lanpr;
+ }
+ }
+ else { /* !value */
+ c->flag &= ~COLLECTION_CONFIGURED_FOR_LANPR;
+ /* CollectionLANPR will be deleted when collection is deleted. */
+ }
+}
+
#else
/* collection.objects */
@@ -382,6 +411,131 @@ static void rna_def_collection_children(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+static void rna_def_collection_lanpr(BlenderRNA *brna)
+{
+ PropertyRNA *prop;
+ StructRNA *srna;
+
+ srna = RNA_def_struct(brna, "CollectionLANPRLineType", NULL);
+ RNA_def_struct_ui_text(srna, "Collection LANPR Line Type", "Collection lanpr line type");
+ RNA_def_struct_sdna(srna, "CollectionLANPRLineType");
+
+ prop = RNA_def_property(srna, "use", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Use", "Use this line type");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "target_layer");
+ RNA_def_property_ui_text(prop, "Layer", "Grease Pencil layer to put the results into");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "material", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "target_material");
+ RNA_def_property_ui_text(
+ prop, "Material", "Grease Pencil material to use to generate the results");
+
+ static const EnumPropertyItem rna_collection_lanpr_usage[] = {
+ {COLLECTION_FEATURE_LINE_INCLUDE,
+ "INCLUDE",
+ 0,
+ "Include",
+ "Collection will produce feature lines"},
+ {COLLECTION_FEATURE_LINE_OCCLUSION_ONLY,
+ "OCCLUSION_ONLY",
+ 0,
+ "Occlusion Only",
+ "Only use the collection to produce occlusion"},
+ {COLLECTION_FEATURE_LINE_EXCLUDE,
+ "EXCLUDE",
+ 0,
+ "Exclude",
+ "Don't use this collection in LANPR"},
+ {0, NULL, 0, NULL, NULL}};
+
+ srna = RNA_def_struct(brna, "CollectionLANPR", NULL);
+ RNA_def_struct_sdna(srna, "CollectionLANPR");
+ RNA_def_struct_ui_text(srna, "Collection LANPR Usage", "LANPR usage for this collection");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "usage", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_collection_lanpr_usage);
+ RNA_def_property_ui_text(prop, "Usage", "How to use this collection in LANPR");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "force", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_COLLECTION_FORCE);
+ RNA_def_property_ui_text(
+ prop, "Force", "Force object that has LANPR modifiers to follow collection usage flag");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "target");
+ RNA_def_property_ui_text(prop, "Target", "Grease Pencil object to put the stroke result");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "replace", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_REPLACE_STROKES);
+ RNA_def_property_ui_text(prop, "Replace", "Replace existing GP frames");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Layer", "Grease Pencil layer to put the results into");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "target_material", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop, "Material", "Grease Pencil material to use to generate the results");
+
+ prop = RNA_def_property(srna, "use_same_style", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_USE_SAME_STYLE);
+ RNA_def_property_ui_text(prop, "Same Style", "Use same style for different types");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "contour", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CollectionLANPRLineType");
+ RNA_def_property_ui_text(prop, "Contour", "Contour line type");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "crease", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CollectionLANPRLineType");
+ RNA_def_property_ui_text(prop, "Crease", "Creaseline type");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "edge_mark", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CollectionLANPRLineType");
+ RNA_def_property_ui_text(prop, "Edge Mark", "Edge mark line type");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CollectionLANPRLineType");
+ RNA_def_property_ui_text(prop, "Material", "Material separate line type");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "intersection", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CollectionLANPRLineType");
+ RNA_def_property_ui_text(prop, "Intersection", "Intersection line type");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "use_multiple_levels", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS);
+ RNA_def_property_ui_text(prop, "Multiple Levels", "Use multiple occlusion levels");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "level_start", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 255);
+ RNA_def_property_ui_range(prop, 0, 255, 1, -1);
+ RNA_def_property_ui_text(prop, "Level Start", "Occlusion level start");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 255);
+ RNA_def_property_ui_range(prop, 0, 255, 1, -1);
+ RNA_def_property_ui_text(prop, "Level End", "Occlusion level end");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+}
+
void RNA_def_collections(BlenderRNA *brna)
{
StructRNA *srna;
@@ -475,6 +629,18 @@ void RNA_def_collections(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1);
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");
+
+ prop = RNA_def_property(srna, "configure_lanpr", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Collection_lanpr_configure_get", "rna_Collection_lanpr_configure_set");
+ RNA_def_property_ui_text(prop, "Configure", "Configure this collection for LANPR");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ rna_def_collection_lanpr(brna);
+
+ prop = RNA_def_property(srna, "lanpr", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CollectionLANPR");
+ RNA_def_property_ui_text(prop, "LANPR", "LANPR settings for the collection");
}
#endif
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index cb59eee6afa..9faf589fe8a 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -165,6 +165,7 @@ void RNA_def_key(struct BlenderRNA *brna);
void RNA_def_light(struct BlenderRNA *brna);
void RNA_def_lattice(struct BlenderRNA *brna);
void RNA_def_linestyle(struct BlenderRNA *brna);
+void RNA_def_lanpr(struct BlenderRNA *brna);
void RNA_def_main(struct BlenderRNA *brna);
void RNA_def_material(struct BlenderRNA *brna);
void RNA_def_mesh(struct BlenderRNA *brna);
diff --git a/source/blender/makesrna/intern/rna_lanpr.c b/source/blender/makesrna/intern/rna_lanpr.c
new file mode 100644
index 00000000000..87db80b0095
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_lanpr.c
@@ -0,0 +1,192 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Blender Foundation (2008).
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_lanpr.c
+ * \ingroup RNA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "BLI_utildefines.h"
+#include "BLI_string_utils.h"
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "DNA_lanpr_types.h"
+#include "DNA_material_types.h"
+#include "DNA_texture_types.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#ifdef RNA_RUNTIME
+
+#else
+
+static void rna_def_lanpr_line_layer(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem rna_enum_lanpr_normal_mode[] = {
+ {LANPR_NORMAL_DIRECTIONAL,
+ "DIRECTIONAL",
+ 0,
+ "Directional",
+ "Use directional vector to control line width"},
+ /* Seems working... */
+ {LANPR_NORMAL_POINT, "POINT", 0, "Point", "Use Point Light Style"},
+ {0, NULL, 0, NULL, NULL}};
+
+ srna = RNA_def_struct(brna, "LANPR_LineLayer", NULL);
+ RNA_def_struct_sdna(srna, "LANPR_LineLayer");
+ RNA_def_struct_ui_text(srna, "Line Layer", "LANPR_LineLayer");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Name of this layer");
+
+ prop = RNA_def_property(srna, "normal_enabled", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_NORMAL_ENABLED);
+ RNA_def_property_ui_text(prop, "Enabled", "Enable normal controlled line weight");
+
+ prop = RNA_def_property(srna, "normal_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_lanpr_normal_mode);
+ RNA_def_property_ui_text(prop, "Normal", "Normal controlled line weight");
+
+ prop = RNA_def_property(srna, "normal_effect_inverse", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_NORMAL_INVERSE);
+ RNA_def_property_ui_text(prop, "Inverse", "Inverse normal effect");
+
+ prop = RNA_def_property(
+ srna, "normal_ramp_begin", PROP_FLOAT, PROP_FACTOR); /* begin is least strength */
+ RNA_def_property_float_default(prop, 0.0f);
+ RNA_def_property_ui_text(prop, "Ramp Begin", "Normal ramp begin value");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05, 2);
+
+ prop = RNA_def_property(srna, "normal_ramp_end", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_ui_text(prop, "Ramp End", "Normal ramp end value");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05, 2);
+
+ prop = RNA_def_property(
+ srna, "normal_thickness_start", PROP_FLOAT, PROP_NONE); /* begin is least strength */
+ RNA_def_property_float_default(prop, 0.2f);
+ RNA_def_property_ui_text(prop, "Thickness Begin", "Normal thickness begin value");
+ RNA_def_property_ui_range(prop, 0.0f, 5.0f, 0.05, 2);
+
+ prop = RNA_def_property(srna, "normal_thickness_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 1.5f);
+ RNA_def_property_ui_text(prop, "Thickness End", "Normal thickness end value");
+ RNA_def_property_ui_range(prop, 0.0f, 5.0f, 0.05, 2);
+
+ prop = RNA_def_property(srna, "normal_control_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Normal style control object");
+
+ prop = RNA_def_property(srna, "use_same_style", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_USE_SAME_STYLE);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(prop, "Same Style", "Use same styles for multiple line types");
+
+ prop = RNA_def_property(srna, "use_multiple_levels", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS);
+ RNA_def_property_ui_text(
+ prop, "Use Multiple Levels", "Select lines from multiple occlusion levels");
+
+ /* types */
+ prop = RNA_def_property(srna, "contour", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "LANPR_LineType");
+ RNA_def_property_ui_text(prop, "Contour", "Contour line type");
+
+ prop = RNA_def_property(srna, "crease", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "LANPR_LineType");
+ RNA_def_property_ui_text(prop, "Crease", "Creaseline type");
+
+ prop = RNA_def_property(srna, "edge_mark", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "LANPR_LineType");
+ RNA_def_property_ui_text(prop, "Edge Mark", "Edge mark line type");
+
+ prop = RNA_def_property(srna, "material_separate", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "LANPR_LineType");
+ RNA_def_property_ui_text(prop, "Material Separate", "Material separate line type");
+
+ prop = RNA_def_property(srna, "intersection", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "LANPR_LineType");
+ RNA_def_property_ui_text(prop, "Intersection", "Intersection line type");
+
+ prop = RNA_def_property(srna, "level_start", PROP_INT, PROP_NONE);
+ RNA_def_property_int_default(prop, 0);
+ RNA_def_property_ui_text(prop, "Level Start", "Occlusion level start");
+ RNA_def_property_range(prop, 0, 128);
+
+ prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE);
+ RNA_def_property_int_default(prop, 0);
+ RNA_def_property_ui_text(prop, "Level End", "Occlusion level end");
+ RNA_def_property_range(prop, 0, 128);
+
+ prop = RNA_def_property(srna, "thickness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_ui_text(prop, "Thickness", "Master Thickness");
+ RNA_def_property_ui_range(prop, 0.0f, 100.0f, 0.1, 2);
+
+ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Color", "Master Color");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 2);
+}
+
+static void rna_def_lanpr_line_type(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "LANPR_LineType", NULL);
+ RNA_def_struct_sdna(srna, "LANPR_LineType");
+ RNA_def_struct_ui_text(srna, "Line Type", "LANPR_LineType");
+
+ prop = RNA_def_property(srna, "use", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Use", "This line type is enabled");
+
+ prop = RNA_def_property(srna, "thickness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_ui_text(prop, "Thickness", "Relative thickness to master");
+ RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.01, 2);
+
+ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Color", "Color of this line type");
+}
+
+void RNA_def_lanpr(BlenderRNA *brna)
+{
+ rna_def_lanpr_line_type(brna);
+ rna_def_lanpr_line_layer(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index a27d77df32a..1fffd6530e6 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -2527,6 +2527,110 @@ static void rna_def_object_display(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Shadow", "Object cast shadows in the 3d viewport");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
}
+static void rna_def_object_lanpr(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ObjectLANPRLineType", NULL);
+ RNA_def_struct_ui_text(srna, "Object LANPR Line Type", "Object lanpr line type");
+ RNA_def_struct_sdna(srna, "ObjectLANPRLineType");
+
+ prop = RNA_def_property(srna, "use", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Use", "Use this line type");
+
+ prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "target_layer");
+ RNA_def_property_ui_text(prop, "Layer", "Grease Pencil layer to put the results into");
+
+ prop = RNA_def_property(srna, "material", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "target_material");
+ RNA_def_property_ui_text(
+ prop, "Material", "Grease Pencil material to use to generate the results");
+
+ static EnumPropertyItem prop_feature_line_usage_items[] = {
+ {OBJECT_FEATURE_LINE_INHERENT,
+ "INHEREIT",
+ 0,
+ "Inhereit",
+ "Follow settings from the parent collection"},
+ {OBJECT_FEATURE_LINE_INCLUDE,
+ "INCLUDE",
+ 0,
+ "Include",
+ "Include this object into LANPR calculation"},
+ {OBJECT_FEATURE_LINE_OCCLUSION_ONLY,
+ "OCCLUSION_ONLY",
+ 0,
+ "Occlusion Only",
+ "Don't produce lines, only used as occlusion object"},
+ {OBJECT_FEATURE_LINE_EXCLUDE,
+ "EXCLUDE",
+ 0,
+ "Exclude",
+ "Don't use this object for LANPR rendering"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "ObjectLANPR", NULL);
+ RNA_def_struct_ui_text(srna, "Object LANPR", "Object lanpr settings");
+ RNA_def_struct_sdna(srna, "ObjectLANPR");
+
+ 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");
+
+ prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "target");
+ RNA_def_property_ui_text(prop, "Target", "GPencil object to put the stroke result");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+
+ prop = RNA_def_property(srna, "replace", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_REPLACE_STROKES);
+ RNA_def_property_ui_text(prop, "Replace", "Replace existing GP frames");
+
+ prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Layer", "Grease Pencil layer to put the results into");
+
+ prop = RNA_def_property(srna, "target_material", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop, "Material", "Grease Pencil material to use to generate the results");
+
+ prop = RNA_def_property(srna, "use_same_style", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_USE_SAME_STYLE);
+ RNA_def_property_ui_text(prop, "Same Style", "Use same style for different line types");
+
+ prop = RNA_def_property(srna, "contour", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ObjectLANPRLineType");
+ RNA_def_property_ui_text(prop, "Contour", "Contour line type");
+
+ prop = RNA_def_property(srna, "crease", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ObjectLANPRLineType");
+ RNA_def_property_ui_text(prop, "Crease", "Creaseline type");
+
+ prop = RNA_def_property(srna, "edge_mark", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ObjectLANPRLineType");
+ RNA_def_property_ui_text(prop, "Edge Mark", "Edge mark line type");
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ObjectLANPRLineType");
+ RNA_def_property_ui_text(prop, "Material", "Material separate line type");
+
+ prop = RNA_def_property(srna, "use_multiple_levels", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_LINE_LAYER_USE_MULTIPLE_LEVELS);
+ RNA_def_property_ui_text(prop, "Multiple Levels", "Use multiple occlusion levels");
+
+ prop = RNA_def_property(srna, "level_start", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 255);
+ RNA_def_property_ui_range(prop, 0, 255, 1, -1);
+ RNA_def_property_ui_text(prop, "Level Start", "Occlusion level start");
+
+ prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 255);
+ RNA_def_property_ui_range(prop, 0, 255, 1, -1);
+ RNA_def_property_ui_text(prop, "Level End", "Occlusion level end");
+}
static void rna_def_object(BlenderRNA *brna)
{
@@ -3286,6 +3390,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");
+ /* LANPR */
+ prop = RNA_def_property(srna, "lanpr", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ObjectLANPR");
+ RNA_def_property_ui_text(prop, "LANPR", "LANPR settings for the object");
+
RNA_api_object(srna);
}
@@ -3298,6 +3407,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_lanpr(brna);
RNA_define_animate_sdna(true);
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index e3d779ce7fb..e65d3eb18b0 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -32,6 +32,8 @@
#include "DNA_screen_types.h" /* TransformOrientation */
#include "DNA_userdef_types.h"
#include "DNA_view3d_types.h"
+#include "DNA_screen_types.h" /* TransformOrientation */
+#include "DNA_lanpr_types.h"
#include "DNA_world_types.h"
#include "IMB_imbuf_types.h"
@@ -46,6 +48,7 @@
#include "BKE_paint.h"
#include "ED_gpencil.h"
+#include "ED_lanpr.h"
#include "ED_object.h"
#include "DRW_engine.h"
@@ -2579,6 +2582,66 @@ static char *rna_UnitSettings_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("unit_settings");
}
+/* lanpr */
+
+void rna_lanpr_active_line_layer_index_range(
+ PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ SceneLANPR *lanpr = (SceneLANPR *)ptr->data;
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&lanpr->line_layers) - 1);
+}
+
+int rna_lanpr_active_line_layer_index_get(PointerRNA *ptr)
+{
+ SceneLANPR *lanpr = (SceneLANPR *)ptr->data;
+ LANPR_LineLayer *ls;
+ int i = 0;
+ for (ls = lanpr->line_layers.first; ls; ls = ls->next) {
+ if (ls == lanpr->active_layer)
+ return i;
+ i++;
+ }
+ return 0;
+}
+
+void rna_lanpr_active_line_layer_index_set(PointerRNA *ptr, int value)
+{
+ SceneLANPR *lanpr = (SceneLANPR *)ptr->data;
+ LANPR_LineLayer *ls;
+ int i = 0;
+ for (ls = lanpr->line_layers.first; ls; ls = ls->next) {
+ if (i == value) {
+ lanpr->active_layer = ls;
+ return;
+ }
+ i++;
+ }
+ lanpr->active_layer = 0;
+}
+
+PointerRNA rna_lanpr_active_line_layer_get(PointerRNA *ptr)
+{
+ SceneLANPR *lanpr = (SceneLANPR *)ptr->data;
+ LANPR_LineLayer *ls = lanpr->active_layer;
+ return rna_pointer_inherit_refine(ptr, &RNA_LANPR_LineLayer, ls);
+}
+
+void rna_lanpr_active_line_layer_set(PointerRNA *ptr, PointerRNA value)
+{
+ SceneLANPR *lanpr = (SceneLANPR *)ptr->data;
+ lanpr->active_layer = value.data;
+}
+
+static bool rna_lanpr_shader_error_get(PointerRNA *UNUSED(ptr))
+{
+# ifdef WITH_LANPR
+ return ED_lanpr_dpix_shader_error();
+# else
+ return false;
+# endif
+}
+
static char *rna_FFmpegSettings_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("render.ffmpeg");
@@ -7242,6 +7305,218 @@ static void rna_def_scene_gpencil(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
+static void rna_def_scene_lanpr(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem rna_enum_lanpr_master_mode[] = {
+ {LANPR_MASTER_MODE_SOFTWARE, "SOFTWARE", 0, "CPU", "Software edge calculation"},
+ {LANPR_MASTER_MODE_DPIX, "DPIX", 0, "GPU", "DPIX GPU edge extraction"},
+ {0, NULL, 0, NULL, NULL}};
+
+ static const EnumPropertyItem rna_enum_lanpr_gpu_cache_size[] = {
+ {LANPR_GPU_CACHE_SIZE_512, "S512", 0, "512", "512px texture as cache"},
+ {LANPR_GPU_CACHE_SIZE_1K, "S1K", 0, "1K", "1K px texture as cache"},
+ {LANPR_GPU_CACHE_SIZE_2K, "S2K", 0, "2K", "2K px texture as cache"},
+ {LANPR_GPU_CACHE_SIZE_4K, "S4K", 0, "4K", "4K px texture as cache"},
+ {LANPR_GPU_CACHE_SIZE_8K, "S8K", 0, "8K", "8K px texture as cache"},
+ {LANPR_GPU_CACHE_SIZE_16K, "S16K", 0, "16K", "16K px texture as cache"},
+ {0, NULL, 0, NULL, NULL}};
+
+ srna = RNA_def_struct(brna, "SceneLANPR", NULL);
+ RNA_def_struct_sdna(srna, "SceneLANPR");
+ RNA_def_struct_ui_text(srna, "Scene LANPR Config", "LANPR global config");
+
+ prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_ENABLED);
+ RNA_def_property_ui_text(prop, "Enabled", "Is LANPR enabled");
+ RNA_def_property_update(prop, NC_WINDOW, NULL);
+
+ prop = RNA_def_property(srna, "auto_update", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_AUTO_UPDATE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(
+ prop, "Auto Update", "Automatically update LANPR cache when frame changes");
+
+ prop = RNA_def_property(srna, "gpencil_overwrite", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_GPENCIL_OVERWRITE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_ui_text(prop,
+ "GPencil Overwrite",
+ "Overwrite existing strokes in the current frame of target GP objects");
+
+ prop = RNA_def_property(srna, "master_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_lanpr_master_mode);
+ RNA_def_property_enum_default(prop, LANPR_MASTER_MODE_DPIX);
+ RNA_def_property_ui_text(prop, "Master Mode", "Choose calculation mode for NPR Line");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "gpu_cache_size", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_lanpr_gpu_cache_size);
+ RNA_def_property_enum_default(prop, LANPR_GPU_CACHE_SIZE_512);
+ RNA_def_property_ui_text(prop, "GPU Cache Size", "Texture cache size for DPIX algorithm");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "depth_width_influence", PROP_FLOAT, PROP_PERCENTAGE);
+ RNA_def_property_float_default(prop, 0.3f);
+ RNA_def_property_ui_text(prop, "Width Influence", "Use camera distance to control line width");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "depth_width_curve", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 0.3f);
+ RNA_def_property_ui_text(prop, "Width Curve", "Width curve");
+ RNA_def_property_ui_range(prop, -5.0f, 0.90f, 0.1f, 1);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "depth_alpha_influence", PROP_FLOAT, PROP_PERCENTAGE);
+ RNA_def_property_float_default(prop, 0.3f);
+ RNA_def_property_ui_text(prop, "Alpha Influence", "Use camera distance to control line alpha");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "depth_alpha_curve", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 0.3f);
+ RNA_def_property_ui_text(prop, "Alpha Curve", "alpha curve");
+ RNA_def_property_ui_range(prop, -5.0f, 0.90f, 0.1f, 1);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "taper_left_distance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 20.0f);
+ RNA_def_property_ui_text(prop, "Left Distance", "Left side taper distance");
+ RNA_def_property_ui_range(prop, 0.0f, 100.0f, 0.1f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "taper_right_distance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 20.0f);
+ RNA_def_property_ui_text(prop, "Right Distance", "Right side taper distance");
+ RNA_def_property_ui_range(prop, 0.0f, 100.0f, 0.1f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "taper_left_strength", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_ui_text(prop, "Left Strength", "Left side taper strength");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "taper_right_strength", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_ui_text(prop, "Right Strength", "Right side taper strength");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "use_same_taper", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_SAME_TAPER);
+ RNA_def_property_ui_text(prop, "Same Taper", "Same/Different taper value");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "line_color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_ui_text(prop, "Line Color", "Drawing lines using this color");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "crease_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 0.5f);
+ RNA_def_property_ui_text(prop, "Crease Threshold", "cosine value of face angle");
+ RNA_def_property_ui_range(prop, -1.0f, 1.0f, 0.01f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "crease_fade_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 0.5f);
+ RNA_def_property_ui_text(prop, "Crease Fade", "cosine value of face angle");
+ RNA_def_property_ui_range(prop, -1.0f, 1.0f, 0.01f, 2);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "use_intersections", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_USE_INTERSECTIONS);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_ui_text(prop, "Calculate Intersections", "Calculate Intersections or not");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "enable_chaining", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LANPR_USE_CHAINING);
+ RNA_def_property_boolean_default(prop, 1);
+ RNA_def_property_ui_text(prop, "Enable Chaining", "Chain Feature Lines After Occlusion Test");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ /* should be read-only */
+ prop = RNA_def_property(srna, "shader_error", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_default(prop, 0);
+ RNA_def_property_boolean_funcs(prop, "rna_lanpr_shader_error_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "DPIX Shader Error", "Can't compile DPIX transform shader on your GPU");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ /* Below these two are only for grease pencil, thus no viewport updates. */
+
+ prop = RNA_def_property(srna, "chaining_geometry_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 0.1f);
+ 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, 1.0f, 0.01f, 3);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+
+ prop = RNA_def_property(srna, "chaining_image_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 0.01f);
+ RNA_def_property_ui_text(
+ prop,
+ "Image Threshold",
+ "Segments where their image distance between them lower 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);
+
+ /* here's the collection stuff.... */
+
+ prop = RNA_def_property(srna, "layers", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "line_layers", NULL);
+ RNA_def_property_struct_type(prop, "LANPR_LineLayer");
+ RNA_def_property_ui_text(prop, "Line Layers", "LANPR Line Layers");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ /* this part I refered to gpencil's and freestyle's and it seems that there's no difference */
+ RNA_def_property_srna(prop, "LineLayers");
+ srna = RNA_def_struct(brna, "LineLayers", NULL);
+ RNA_def_struct_sdna(srna, "SceneLANPR");
+ RNA_def_struct_ui_text(srna, "LANPR Line Layers", "");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "active_layer", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "LANPR_LineLayer");
+ RNA_def_property_pointer_funcs(
+ prop, "rna_lanpr_active_line_layer_get", "rna_lanpr_active_line_layer_set", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Line Layer", "Active line layer being displayed");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+
+ prop = RNA_def_property(srna, "active_layer_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop,
+ "rna_lanpr_active_line_layer_index_get",
+ "rna_lanpr_active_line_layer_index_set",
+ "rna_lanpr_active_line_layer_index_range");
+ RNA_def_property_ui_text(prop, "Active Line Layer Index", "Index of active line layer slot");
+ RNA_def_property_update(prop, NC_SCENE, NULL);
+}
+
void RNA_def_scene(BlenderRNA *brna)
{
StructRNA *srna;
@@ -7715,6 +7990,11 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "SceneEEVEE");
RNA_def_property_ui_text(prop, "EEVEE", "EEVEE settings for the scene");
+ /* LANPR */
+ prop = RNA_def_property(srna, "lanpr", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SceneLANPR");
+ RNA_def_property_ui_text(prop, "LANPR", "LANPR settings for the scene");
+
/* Grease Pencil */
prop = RNA_def_property(srna, "grease_pencil_settings", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "SceneGpencil");
@@ -7737,6 +8017,7 @@ void RNA_def_scene(BlenderRNA *brna)
rna_def_display_safe_areas(brna);
rna_def_scene_display(brna);
rna_def_scene_eevee(brna);
+ rna_def_scene_lanpr(brna);
rna_def_view_layer_eevee(brna);
rna_def_scene_gpencil(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 df0d514fabf..a7c2feebc40 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -428,13 +428,14 @@ static const EnumPropertyItem buttons_context_items[] = {
{BCONTEXT_OUTPUT, "OUTPUT", ICON_OUTPUT, "Output", "Output Properties"},
{BCONTEXT_VIEW_LAYER, "VIEW_LAYER", ICON_RENDER_RESULT, "View Layer", "View Layer Properties"},
{BCONTEXT_WORLD, "WORLD", ICON_WORLD, "World", "World Properties"},
+ {BCONTEXT_COLLECTION, "COLLECTION", ICON_GROUP, "Collection", "Collection Properties"},
{BCONTEXT_OBJECT, "OBJECT", ICON_OBJECT_DATA, "Object", "Object Properties"},
{BCONTEXT_CONSTRAINT,
"CONSTRAINT",
ICON_CONSTRAINT,
"Constraints",
- "Object Constraint Properties"},
- {BCONTEXT_MODIFIER, "MODIFIER", ICON_MODIFIER, "Modifiers", "Modifier Properties"},
+ "Object Constraints Properties"},
+ {BCONTEXT_MODIFIER, "MODIFIER", ICON_MODIFIER, "Modifiers", "Modifiers Properties"},
{BCONTEXT_DATA, "DATA", ICON_NONE, "Data", "Object Data Properties"},
{BCONTEXT_BONE, "BONE", ICON_BONE_DATA, "Bone", "Bone Properties"},
{BCONTEXT_BONE_CONSTRAINT,
@@ -443,6 +444,7 @@ static const EnumPropertyItem buttons_context_items[] = {
"Bone Constraints",
"Bone Constraint Properties"},
{BCONTEXT_MATERIAL, "MATERIAL", ICON_MATERIAL, "Material", "Material Properties"},
+ {BCONTEXT_LANPR, "LANPR", ICON_SHADING_RENDERED, "LANPR", "LANPR Properties"},
{BCONTEXT_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture Properties"},
{BCONTEXT_PARTICLE, "PARTICLES", ICON_PARTICLES, "Particles", "Particle Properties"},
{BCONTEXT_PHYSICS, "PHYSICS", ICON_PHYSICS, "Physics", "Physics Properties"},
@@ -1810,6 +1812,14 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(bContext *UNUSE
RNA_enum_item_add_separator(&item, &totitem);
}
+ if (sbuts->pathflag & (1 << BCONTEXT_COLLECTION)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_COLLECTION);
+ }
+
+ if (totitem) {
+ RNA_enum_item_add_separator(&item, &totitem);
+ }
+
if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) {
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OBJECT);
}
@@ -1851,6 +1861,10 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(bContext *UNUSE
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MATERIAL);
}
+ if (sbuts->pathflag & (1 << BCONTEXT_LANPR)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_LANPR);
+ }
+
if (totitem) {
RNA_enum_item_add_separator(&item, &totitem);
}
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 769618005af..f1328ec430f 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -202,6 +202,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_LANPR)
+ add_definitions(-DWITH_LANPR)
+endif()
+
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index cc97f6dec94..e01d12e1c0c 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -36,6 +36,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{"cycles", NULL},
{"cycles_osl", NULL},
{"freestyle", NULL},
+ {"lanpr", NULL},
{"image_cineon", NULL},
{"image_dds", NULL},
{"image_hdr", NULL},
@@ -132,6 +133,12 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
+#ifdef WITH_LANPR
+ SetObjIncref(Py_True);
+#else
+ SetObjIncref(Py_False);
+#endif
+
#ifdef WITH_CINEON
SetObjIncref(Py_True);
#else