diff options
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 |