diff options
author | Himanshi Kalra <himanshikalra98@gmail.com> | 2020-12-17 15:06:26 +0300 |
---|---|---|
committer | Himanshi Kalra <himanshikalra98@gmail.com> | 2020-12-17 15:06:26 +0300 |
commit | 167eace0e73bef77cf362af04f1f0dd47c492a92 (patch) | |
tree | 715217755512456fab8dfa8e38f63d5065a1ccc1 | |
parent | f066bf923c234f52a6159d51f4978bf1072edaf1 (diff) | |
parent | cf2ebaf27c78b3f8f79d9d014ca2261228f87e70 (diff) |
Merge branch 'master' into soc-2020-testing-frameworks
231 files changed, 4548 insertions, 1041 deletions
diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp index fdfde13b915..86f3a0a31fb 100644 --- a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp @@ -142,7 +142,8 @@ const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDi } static string path = ""; - string command = string("xdg-user-dir ") + type_str; + /* Pipe stderr to /dev/null to avoid error prints. We will fail gracefully still. */ + string command = string("xdg-user-dir ") + type_str + " 2> /dev/null"; FILE *fstream = popen(command.c_str(), "r"); if (fstream == NULL) { @@ -163,7 +164,7 @@ const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDi } path = path_stream.str(); - return (const GHOST_TUns8 *)path.c_str(); + return path[0] ? (const GHOST_TUns8 *)path.c_str() : NULL; } const GHOST_TUns8 *GHOST_SystemPathsUnix::getBinaryDir() const diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index d2aebab026f..2bf1d0c2d35 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1614,7 +1614,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, break; } case WT_PACKET: - window->updatePendingWintabEvents(); + window->updateWintabEventsSyncTime(); break; //////////////////////////////////////////////////////////////////////// // Pointer events, processed diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index af02663985d..a4cbf66b22d 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -1292,7 +1292,7 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3 GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); - updatePendingWintabEvents(); + updateWintabEvents(); auto &pendingEvents = m_wintab.pendingEvents; size_t pendingEventSize = pendingEvents.size(); @@ -1388,6 +1388,44 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3 return GHOST_kSuccess; } +void GHOST_WindowWin32::updateWintabEvents() +{ + readWintabEvents(); + // When a Wintab device is used to leave window focus, some of it's packets are periodically not + // queued in time to be flushed. Reading packets needs to occur before expiring packets to clear + // these from the queue. + expireWintabEvents(); +} + +void GHOST_WindowWin32::updateWintabEventsSyncTime() +{ + readWintabEvents(); + + if (!m_wintab.pendingEvents.empty()) { + auto lastEvent = m_wintab.pendingEvents.back(); + m_wintab.sysTimeOffset = ::GetTickCount() - lastEvent.pkTime; + } + + expireWintabEvents(); +} + +void GHOST_WindowWin32::readWintabEvents() +{ + if (!(m_wintab.packetsGet && m_wintab.context)) { + return; + } + + auto &pendingEvents = m_wintab.pendingEvents; + + /* Get new packets. */ + const int numPackets = m_wintab.packetsGet( + m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data()); + + for (int i = 0; i < numPackets; i++) { + pendingEvents.push(m_wintab.pkts[i]); + } +} + /* Wintab (per documentation but may vary with implementation) does not update when its event * buffer is full. This is an issue because we need some synchronization point between Wintab * events and Win32 events, so we can't drain and process the queue immediately. We need to @@ -1396,17 +1434,12 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3 * mode from the Wintab API alone. There is no guaranteed ordering between Wintab and Win32 mouse * events and no documented time stamp shared between the two, so we synchronize on mouse button * events. */ -void GHOST_WindowWin32::updatePendingWintabEvents() +void GHOST_WindowWin32::expireWintabEvents() { - if (!(m_wintab.packetsGet && m_wintab.context)) { - return; - } - auto &pendingEvents = m_wintab.pendingEvents; - /* Clear outdated events from queue. */ - DWORD currTime = ::GetTickCount(); - DWORD millisTimeout = 500; + DWORD currTime = ::GetTickCount() - m_wintab.sysTimeOffset; + DWORD millisTimeout = 300; while (!pendingEvents.empty()) { DWORD pkTime = pendingEvents.front().pkTime; @@ -1417,24 +1450,6 @@ void GHOST_WindowWin32::updatePendingWintabEvents() break; } } - - /* Get new packets. */ - const int numPackets = m_wintab.packetsGet( - m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data()); - - int i = 0; - /* Don't queue outdated packets, such events can include packets that occurred before the current - * window lost and regained focus. */ - for (; i < numPackets; i++) { - DWORD pkTime = m_wintab.pkts[i].pkTime; - - if (currTime < pkTime + millisTimeout) { - break; - } - } - for (; i < numPackets; i++) { - pendingEvents.push(m_wintab.pkts[i]); - } } GHOST_TUns16 GHOST_WindowWin32::getDPIHint() diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index a761d7d84dc..829bbdea051 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -486,9 +486,14 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TSuccess getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo); /** - * Updates stored pending Wintab events. + * Updates pending Wintab events and syncs Wintab time with OS time. */ - void updatePendingWintabEvents(); + void updateWintabEventsSyncTime(); + + /** + * Updates pending Wintab events. + */ + void updateWintabEvents(); GHOST_TSuccess beginFullScreen() const { @@ -629,6 +634,7 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TUns8 numSysButtons = 0; /** Cursors currently in contact mapped to system buttons */ DWORD sysButtonsPressed = 0; + DWORD sysTimeOffset = 0; LONG maxPressure = 0; LONG maxAzimuth = 0, maxAltitude = 0; /** Reusable buffer to read in Wintab Packets. */ @@ -642,6 +648,10 @@ class GHOST_WindowWin32 : public GHOST_Window { */ void initializeWintab(); + void readWintabEvents(); + + void expireWintabEvents(); + /** * Convert Wintab system mapped (mouse) buttons into Ghost button mask. * \param cursor: The Wintab cursor associated to the button. diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 9e40c01dffd3f720b23b906d20df8e999d34a4a +Subproject 877a343fed9613d8e02e7fe7181d3bbb628506f diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 1a3f127714e8da9f0af12d9a174dae9793ae63c +Subproject a3fa40ec0ba525bc96cbfad49f854a0230b0524 diff --git a/release/scripts/modules/bl_keymap_utils/io.py b/release/scripts/modules/bl_keymap_utils/io.py index 091cdbc2642..645a145f994 100644 --- a/release/scripts/modules/bl_keymap_utils/io.py +++ b/release/scripts/modules/bl_keymap_utils/io.py @@ -222,12 +222,21 @@ def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False): fw("]\n") fw("\n\n") fw("if __name__ == \"__main__\":\n") + + # We could remove this in the future, as loading new key-maps in older Blender versions + # makes less and less sense as Blender changes. + fw(" # Only add keywords that are supported.\n") + fw(" from bpy.app import version as blender_version\n") + fw(" keywords = {}\n") + fw(" if blender_version >= (2, 92, 0):\n") + fw(" keywords[\"keyconfig_version\"] = keyconfig_version\n") + fw(" import os\n") fw(" from bl_keymap_utils.io import keyconfig_import_from_data\n") fw(" keyconfig_import_from_data(\n") fw(" os.path.splitext(os.path.basename(__file__))[0],\n") fw(" keyconfig_data,\n") - fw(" keyconfig_version=keyconfig_version,\n") + fw(" **keywords,\n") fw(" )\n") diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 5a5957b8bf1..d3990851e2c 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1217,7 +1217,7 @@ def km_view3d(params): ("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.use_snap')]}), - op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", False)]), + op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", True)]), ("object.transform_axis_target", {"type": 'T', "value": 'PRESS', "shift": True}, None), ("transform.skin_resize", {"type": 'A', "value": 'PRESS', "ctrl": True}, None), ]) @@ -2501,6 +2501,8 @@ def km_sequencer(params): ("sequencer.delete", {"type": 'DEL', "value": 'PRESS'}, None), ("sequencer.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), + ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True, "shift": True}, + {"properties": [("keep_offset", True)]}), ("sequencer.images_separate", {"type": 'Y', "value": 'PRESS'}, None), ("sequencer.meta_toggle", {"type": 'TAB', "value": 'PRESS'}, None), ("sequencer.meta_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py index c317df78aa5..317555280e5 100644 --- a/release/scripts/startup/bl_operators/assets.py +++ b/release/scripts/startup/bl_operators/assets.py @@ -22,6 +22,7 @@ from bpy_extras.asset_utils import ( SpaceAssetInfo, ) + class ASSET_OT_tag_add(bpy.types.Operator): """Add a new keyword tag to the active asset""" diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index 7d7e3793dba..b94104d10a9 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -18,6 +18,7 @@ import bpy + def geometry_node_group_empty_new(context): group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree') group.inputs.new('NodeSocketGeometry', "Geometry") @@ -33,6 +34,7 @@ def geometry_node_group_empty_new(context): return group + def geometry_modifier_poll(context) -> bool: ob = context.object @@ -42,6 +44,7 @@ def geometry_modifier_poll(context) -> bool: return True + class NewGeometryNodesModifier(bpy.types.Operator): """Create a new modifier with a new geometry node group""" diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 8c275da28ac..0600536cb66 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -573,6 +573,7 @@ class QuickLiquid(Operator): return {'FINISHED'} + classes = ( QuickExplode, QuickFur, diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 8201ce080b1..c663a736441 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -893,6 +893,7 @@ class GreasePencilLayerDisplayPanel: row = layout.row(align=True) row.prop(gpl, "use_solo_mode", text="Show Only on Keyframed") + class GreasePencilFlipTintColors(Operator): bl_label = "Flip Colors" bl_idname = "gpencil.tint_flip" diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 7e9e15c30da..5d241e8e216 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -550,7 +550,6 @@ def brush_settings(layout, context, brush, popover=False): if context.preferences.experimental.use_sculpt_tools_tilt and capabilities.has_tilt: layout.prop(brush, "tilt_strength_factor", slider=True) - row = layout.row(align=True) row.prop(brush, "hardness", slider=True) row.prop(brush, "invert_hardness_pressure", text="") @@ -765,6 +764,10 @@ def brush_settings(layout, context, brush, popover=False): col.prop(brush, "surface_smooth_current_vertex") col.prop(brush, "surface_smooth_iterations") + elif sculpt_tool == 'DISPLACEMENT_SMEAR': + col = layout.column() + col.prop(brush, "smear_deform_type") + elif sculpt_tool == 'MASK': layout.row().prop(brush, "mask_tool", expand=True) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 527a5bc623e..98b155d8a21 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -267,7 +267,11 @@ class FILEBROWSER_PT_bookmarks_system(Panel): @classmethod def poll(cls, context): - return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) + return ( + not context.preferences.filepaths.hide_system_bookmarks and + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -301,7 +305,10 @@ class FILEBROWSER_PT_bookmarks_favorites(Panel): @classmethod def poll(cls, context): - return panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) + return ( + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -338,7 +345,11 @@ class FILEBROWSER_PT_bookmarks_recents(Panel): @classmethod def poll(cls, context): - return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) + return ( + not context.preferences.filepaths.hide_recent_locations and + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -362,7 +373,11 @@ class FILEBROWSER_PT_advanced_filter(Panel): @classmethod def poll(cls, context): # only useful in append/link (library) context currently... - return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) + return ( + context.space_data.params.use_library_browsing and + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -573,23 +588,33 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): active_file = context.active_file active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) - layout.use_property_split = True - if not active_file or not active_asset: - layout.label(text="No asset selected.") + layout.label(text="No asset selected.", icon='INFO') return - box = layout.box() + # If the active file is an ID, use its name directly so renaming is possible from right here. + layout.prop(context.id if context.id is not None else active_file, "name", text="") + + +class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): + bl_label = "Preview" + + def draw(self, context): + layout = self.layout + active_file = context.active_file + + row = layout.row() + box = row.box() box.template_icon(icon_value=active_file.preview_icon_id, scale=5.0) if bpy.ops.ed.lib_id_load_custom_preview.poll(): - box.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="Load Custom") - layout.prop(active_file, "name") + col = row.column(align=True) + col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="") + col.separator() + col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="") -class ASSETBROWSER_PT_metadata_details(asset_utils.AssetBrowserPanel, Panel): - bl_region_type = 'TOOL_PROPS' +class ASSETBROWSER_PT_metadata_details(asset_utils.AssetMetaDataPanel, Panel): bl_label = "Details" - bl_parent_id = "ASSETBROWSER_PT_metadata" def draw(self, context): layout = self.layout @@ -647,6 +672,7 @@ classes = ( FILEBROWSER_MT_context_menu, ASSETBROWSER_PT_navigation_bar, ASSETBROWSER_PT_metadata, + ASSETBROWSER_PT_metadata_preview, ASSETBROWSER_PT_metadata_details, ASSETBROWSER_PT_metadata_tags, ASSETBROWSER_UL_metadata_tags, diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index f4e88b70281..342b72acb8c 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -1465,7 +1465,7 @@ class IMAGE_PT_overlay(Panel): bl_ui_units_x = 13 def draw(self, context): - pass + pass class IMAGE_PT_overlay_uv_edit(Panel): @@ -1496,7 +1496,6 @@ class IMAGE_PT_overlay_uv_edit(Panel): subrow.prop(uvedit, "display_stretch_type", text="") - class IMAGE_PT_overlay_uv_edit_geometry(Panel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'HEADER' @@ -1529,7 +1528,6 @@ class IMAGE_PT_overlay_uv_edit_geometry(Panel): row.prop(uvedit, "show_faces", text="Faces") - class IMAGE_PT_overlay_texture_paint(Panel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'HEADER' diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 1c4d1919f43..e6af83b61f4 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -168,7 +168,6 @@ class NODE_HT_header(Header): else: row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier") - else: # Custom node tree is edited as independent ID block NODE_MT_editor_menus.draw_collapsible(context, layout) diff --git a/release/scripts/startup/bl_ui/space_properties.py b/release/scripts/startup/bl_ui/space_properties.py index 0f64ab63d6b..765cab1ace2 100644 --- a/release/scripts/startup/bl_ui/space_properties.py +++ b/release/scripts/startup/bl_ui/space_properties.py @@ -61,8 +61,10 @@ class PROPERTIES_PT_navigation_bar(Panel): layout.scale_x = 1.4 layout.scale_y = 1.4 if view.search_filter: - layout.prop_tabs_enum(view, "context", data_highlight=view, - property_highlight="tab_search_results", icon_only=True) + layout.prop_tabs_enum( + view, "context", data_highlight=view, + property_highlight="tab_search_results", icon_only=True, + ) else: layout.prop_tabs_enum(view, "context", icon_only=True) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 3f92fce81f6..e79b5d3e2c8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -129,6 +129,8 @@ class SEQUENCER_HT_header(Header): layout = self.layout st = context.space_data + scene = context.scene + sequencer_tool_settings = context.tool_settings.sequencer_tool_settings show_region_tool_header = st.show_region_tool_header @@ -139,12 +141,17 @@ class SEQUENCER_HT_header(Header): SEQUENCER_MT_editor_menus.draw_collapsible(context, layout) - if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: - + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: + layout.separator_spacer() + row = layout.row(align=True) + row.prop(sequencer_tool_settings, "fit_method", text="") layout.separator_spacer() - layout.prop(st, "display_mode", text="", icon_only=True) + if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: + if st.view_type == 'PREVIEW': + layout.separator_spacer() + layout.prop(st, "display_mode", text="", icon_only=True) layout.prop(st, "preview_channels", text="", icon_only=True) gpd = context.gpencil_data @@ -157,6 +164,12 @@ class SEQUENCER_HT_header(Header): if tool_settings.use_proportional_edit: row.prop(tool_settings, "proportional_edit_falloff", icon_only=True) + row = layout.row(align=True) + row.prop(st, "show_strip_overlay", text="", icon='OVERLAY') + sub = row.row(align=True) + sub.popover(panel="SEQUENCER_PT_overlay", text="") + sub.active = st.show_strip_overlay + class SEQUENCER_MT_editor_menus(Menu): bl_idname = "SEQUENCER_MT_editor_menus" @@ -176,6 +189,80 @@ class SEQUENCER_MT_editor_menus(Menu): layout.menu("SEQUENCER_MT_strip") +class SEQUENCER_PT_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Overlays" + bl_ui_units_x = 7 + + def draw(self, _context): + pass + + +class SEQUENCER_PT_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Overlays" + bl_ui_units_x = 7 + + def draw(self, _context): + pass + + +class SEQUENCER_PT_preview_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_parent_id = 'SEQUENCER_PT_overlay' + bl_label = "Preview Overlays" + + @classmethod + def poll(cls, context): + st = context.space_data + return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} and st.display_mode == 'IMAGE' + + def draw(self, context): + ed = context.scene.sequence_editor + st = context.space_data + layout = self.layout + + layout.active = st.show_strip_overlay + layout.prop(ed, "show_overlay", text="Frame Overlay") + layout.prop(st, "show_safe_areas", text="Safe Areas") + layout.prop(st, "show_metadata", text="Metadata") + layout.prop(st, "show_annotation", text="Annotations") + + +class SEQUENCER_PT_sequencer_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_parent_id = 'SEQUENCER_PT_overlay' + bl_label = "Sequencer Overlays" + + @classmethod + def poll(cls, context): + st = context.space_data + return st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'} + + def draw(self, context): + st = context.space_data + layout = self.layout + + layout.active = st.show_strip_overlay + + layout.prop(st, "show_strip_name", text="Name") + layout.prop(st, "show_strip_source", text="Source") + layout.prop(st, "show_strip_duration", text="Duration") + + layout.separator() + + layout.prop(st, "show_strip_offset", text="Offsets") + layout.prop(st, "show_fcurves", text="F-Curves") + + layout.separator() + + layout.prop_menu_enum(st, "waveform_display_type") + + class SEQUENCER_MT_view_cache(Menu): bl_label = "Cache" @@ -294,6 +381,12 @@ class SEQUENCER_MT_view(Menu): layout.operator("view2d.zoom_border", text="Zoom") layout.menu("SEQUENCER_MT_preview_zoom") + if st.display_mode == 'IMAGE': + layout.prop(st, "use_zoom_to_fit") + elif st.display_mode == 'WAVEFORM': + layout.separator() + layout.prop(st, "show_separate_color", text="Show Separate Color Channels") + layout.separator() layout.menu("SEQUENCER_MT_proxy") @@ -318,22 +411,8 @@ class SEQUENCER_MT_view(Menu): layout.separator() layout.prop(st, "show_seconds") - layout.prop(st, "show_strip_offset") - layout.prop(st, "show_fcurves") layout.prop(st, "show_markers") layout.menu("SEQUENCER_MT_view_cache", text="Show Cache") - layout.prop_menu_enum(st, "waveform_display_type", text="Show Waveforms") - - if is_preview: - layout.separator() - if st.display_mode == 'IMAGE': - layout.prop(st, "use_zoom_to_fit") - layout.prop(ed, "show_overlay", text="Show Frame Overlay") - layout.prop(st, "show_safe_areas", text="Show Safe Areas") - layout.prop(st, "show_metadata", text="Show Metadata") - layout.prop(st, "show_annotation", text="Show Annotations") - elif st.display_mode == 'WAVEFORM': - layout.prop(st, "show_separate_color", text="Show Separate Color Channels") layout.separator() @@ -629,6 +708,22 @@ class SEQUENCER_MT_add_effect(Menu): col.enabled = selected_sequences_len(context) != 0 +class SEQUENCER_MT_strip_image_transform(Menu): + bl_label = "Image Transform" + + def draw(self, _context): + layout = self.layout + + layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT' + layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL' + layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH' + layout.separator() + + layout.operator("sequencer.strip_transform_clear", text="Clear Position").property = 'POSITION' + layout.operator("sequencer.strip_transform_clear", text="Clear Scale").property = 'SCALE' + layout.operator("sequencer.strip_transform_clear", text="Clear Rotation").property = 'ROTATION' + layout.operator("sequencer.strip_transform_clear", text="Clear All").property = 'ALL' + class SEQUENCER_MT_strip_transform(Menu): bl_label = "Transform" @@ -723,6 +818,7 @@ class SEQUENCER_MT_strip(Menu): layout.separator() layout.menu("SEQUENCER_MT_strip_transform") + layout.menu("SEQUENCER_MT_strip_image_transform") layout.separator() layout.operator("sequencer.split", text="Split").type = 'SOFT' @@ -2214,6 +2310,7 @@ classes = ( SEQUENCER_MT_strip_effect, SEQUENCER_MT_strip_movie, SEQUENCER_MT_strip, + SEQUENCER_MT_strip_image_transform, SEQUENCER_MT_strip_transform, SEQUENCER_MT_strip_input, SEQUENCER_MT_strip_lock_mute, @@ -2222,6 +2319,10 @@ classes = ( SEQUENCER_PT_active_tool, SEQUENCER_PT_strip, + SEQUENCER_PT_overlay, + SEQUENCER_PT_preview_overlay, + SEQUENCER_PT_sequencer_overlay, + SEQUENCER_PT_effect, SEQUENCER_PT_scene, SEQUENCER_PT_mask, diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 16b02db9377..3b9ce5311f3 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -297,6 +297,7 @@ class TIME_PT_keyframing_settings(TimelinePanelButtons, Panel): col.label(text="New Keyframe Type") col.prop(tool_settings, "keyframe_type", text="") + class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel): bl_label = "Auto Keyframing" bl_options = {'HIDE_HEADER'} diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index a69e3974193..0411d5c64bc 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2903,10 +2903,10 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sequencer_generic.blade, ], 'SEQUENCER_PREVIEW': [ - _defs_sequencer_generic.sample, *_tools_select, - *_tools_annotate, _defs_sequencer_generic.blade, + _defs_sequencer_generic.sample, + *_tools_annotate, ], } diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index ae45b9799f1..0167f0b1e20 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -211,7 +211,7 @@ class USERPREF_PT_interface_display(InterfacePanel, CenterAlignMixIn, Panel): col.separator() col = layout.column(heading="Tooltips", align=True) - col.prop(view, "show_tooltips", text = "User Tooltips") + col.prop(view, "show_tooltips", text="User Tooltips") sub = col.column() sub.active = view.show_tooltips sub.prop(view, "show_tooltips_python") @@ -1362,14 +1362,15 @@ class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel): row.separator() row.label(text="Path") - subrow = row.row() - subrow.operator("preferences.asset_library_add", text="", icon='ADD', emboss=False) for i, library in enumerate(paths.asset_libraries): name_col.prop(library, "name", text="") row = path_col.row() row.prop(library, "path", text="") row.operator("preferences.asset_library_remove", text="", icon='X', emboss=False).index = i + row = box.row() + row.alignment = 'LEFT' + row.operator("preferences.asset_library_add", text="", icon='ADD', emboss=False) # ----------------------------------------------------------------------------- @@ -1881,12 +1882,12 @@ class USERPREF_PT_addons(AddOnPanel, Panel): is_visible = is_visible and is_enabled if is_visible: - if search and search not in info["name"].lower(): - if info["author"]: - if search not in info["author"].lower(): - continue - else: - continue + if search and not ( + (search in info["name"].lower()) or + (info["author"] and (search in info["author"].lower())) or + ((filter == "All") and (search in info["category"].lower())) + ): + continue # Skip 2.7x add-ons included with Blender, unless in debug mode. is_addon_27x = info.get("blender", (0,)) < (2, 80) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index c97d5601a10..8b5183faf62 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -5306,6 +5306,7 @@ class VIEW3D_MT_sculpt_mask_edit_pie(Menu): op.filter_type = 'CONTRAST_DECREASE' op.auto_iteration_count = False + class VIEW3D_MT_sculpt_automasking_pie(Menu): bl_label = "Automasking" diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 293d55a6015..577f9678a62 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -966,6 +966,7 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel): row.active = mesh.use_mirror_vertex_group_x row.prop(mesh, "use_mirror_topology") + class VIEW3D_PT_tools_weightpaint_symmetry_for_topbar(Panel): bl_space_type = 'TOPBAR' bl_region_type = 'HEADER' diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f1b783f31a5..c3ab1b3db97 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -481,10 +481,11 @@ def not_implemented_node(idname): geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ - NodeItem("GeometryNodeRandomAttribute"), + NodeItem("GeometryNodeAttributeRandomize"), NodeItem("GeometryNodeAttributeMath"), NodeItem("GeometryNodeAttributeFill"), NodeItem("GeometryNodeAttributeMix"), + NodeItem("GeometryNodeAttributeColorRamp"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeValToRGB"), diff --git a/release/steam/README.md b/release/steam/README.md new file mode 100644 index 00000000000..05eda799c3f --- /dev/null +++ b/release/steam/README.md @@ -0,0 +1,70 @@ +Creating Steam builds for Blender +================================= + +This script automates creation of the Steam files: download of the archives, +extraction of the archives, preparation of the build scripts (VDF files), actual +building of the Steam game files. + +Requirements +============ + +* MacOS machine - Tested on Catalina 10.15.6. Extracting contents from the DMG + archive did not work Windows nor on Linux using 7-zip. All DMG archives tested + failed to be extracted. As such only MacOS is known to work. +* Steam SDK downloaded from SteamWorks - The `steamcmd` is used to generate the + Steam game files. The path to the `steamcmd` is what is actually needed. +* SteamWorks credentials - Needed to log in using `steamcmd`. +* Login to SteamWorks with the `steamcmd` from the command-line at least once - + Needded to ensure the user is properly logged in. On a new machine the user + will have to go through two-factor authentication. +* App ID and Depot IDs - Needed to create the VDF files. +* Python 3.x - 3.7 was tested. +* Base URL - for downloading the archives. + +Usage +===== + +```bash +$ export STEAMUSER=SteamUserName +$ export STEAMPW=SteamUserPW +$ export BASEURL=https://download.blender.org/release/Blender2.83/ +$ export VERSION=2.83.3 +$ export APPID=appidnr +$ export WINID=winidnr +$ export LINID=linuxidnr +$ export MACOSID=macosidnr + +# log in to SteamWorks from command-line at least once + +$ ../sdk/tools/ContentBuilder/builder_osx/steamcmd +login $STEAMUSER $STEAMPW + +# once that has been done we can now actually start our tool + +$ python3.7 create_steam_builds.py --baseurl $BASEURL --version $VERSION --appid $APPID --winid $WINID --linuxid $LINID --macosid $MACOSID --steamuser $STEAMUSER --steampw $STEAMPW --steamcmd ../sdk/tools/ContentBuilder/builder_osx/steamcmd +``` + +All arguments in the above example are required. + +At the start the tool will login using `steamcmd`. This is necessary to let the +Steam SDK update itself if necessary. + +There are a few optional arguments: + +* `--dryrun`: If set building the game files will not actually happen. A set of + log files and a preview manifest per depot will be created in the output folder. + This can be used to double-check everything works as expected. +* `--skipdl`: If set will skip downloading of the archives. The tool expects the + archives to already exist in the correct content location. +* `--skipextract`: If set will skip extraction of all archives. The tool expects + the archives to already have been correctly extracted in the content location. + +Run the tool with `-h` for detailed information on each argument. + +The content and output folders are generated through appending the version +without dots to the words `content` and `output` respectively, e.g. `content2833` +and `output2833`. These folders are created next to the tool. + +From all `.template` files the Steam build scripts will be generated also in the +same directory as the tool. The files will have the extension `.vdf`. + +In case of errors the tool will have a non-zero return code.
\ No newline at end of file diff --git a/release/steam/blender_app_build.vdf.template b/release/steam/blender_app_build.vdf.template new file mode 100644 index 00000000000..9e2d0625d72 --- /dev/null +++ b/release/steam/blender_app_build.vdf.template @@ -0,0 +1,17 @@ +"appbuild" +{ + "appid" "[APPID]" + "desc" "Blender [VERSION]" // description for this build + "buildoutput" "./[OUTPUT]" // build output folder for .log, .csm & .csd files, relative to location of this file + "contentroot" "./[CONTENT]" // root content folder, relative to location of this file + "setlive" "" // branch to set live after successful build, non if empty + "preview" "[DRYRUN]" // 1 to enable preview builds, 0 to commit build to steampipe + "local" "" // set to flie path of local content server + + "depots" + { + "[WINID]" "depot_build_win.vdf" + "[LINUXID]" "depot_build_linux.vdf" + "[MACOSID]" "depot_build_macos.vdf" + } +} diff --git a/release/steam/create_steam_builds.py b/release/steam/create_steam_builds.py new file mode 100644 index 00000000000..2ecd0c347f7 --- /dev/null +++ b/release/steam/create_steam_builds.py @@ -0,0 +1,397 @@ +#!/usr/bin/env python3 + +import argparse +import pathlib +import requests +import shutil +import subprocess +from typing import Callable, Iterator, List, Tuple + +# supported archive and platform endings, used to create actual archive names +archive_endings = ["windows64.zip", "linux64.tar.xz", "macOS.dmg"] + + +def add_optional_argument(option: str, help: str) -> None: + global parser + """Add an optional argument + + Args: + option (str): Option to add + help (str): Help description for the argument + """ + parser.add_argument(option, help=help, action='store_const', const=1) + + +def blender_archives(version: str) -> Iterator[str]: + """Generator for Blender archives for version. + + Yields for items in archive_endings an archive name in the form of + blender-{version}-{ending}. + + Args: + version (str): Version string of the form 2.83.2 + + + Yields: + Iterator[str]: Name in the form of blender-{version}-{ending} + """ + global archive_endings + + for ending in archive_endings: + yield f"blender-{version}-{ending}" + + +def get_archive_type(archive_type: str, version: str) -> str: + """Return the archive of given type and version. + + Args: + archive_type (str): extension for archive type to check for + version (str): Version string in the form 2.83.2 + + Raises: + Exception: Execption when archive type isn't found + + Returns: + str: archive name for given type + """ + + for archive in blender_archives(version): + if archive.endswith(archive_type): + return archive + raise Exception("Unknown archive type") + + +def execute_command(cmd: List[str], name: str, errcode: int, cwd=".", capture_output=True) -> str: + """Execute the given command. + + Returns the process stdout upon success if any. + + On error print message the command with name that has failed. Print stdout + and stderr of the process if any, and then exit with given error code. + + Args: + cmd (List[str]): Command in list format, each argument as their own item + name (str): Name of command to use when printing to command-line + errcode (int): Error code to use in case of exit() + cwd (str, optional): Folder to use as current work directory for command + execution. Defaults to ".". + capture_output (bool, optional): Whether to capture command output or not. + Defaults to True. + + Returns: + str: stdout if any, or empty string + """ + cmd_process = subprocess.run( + cmd, capture_output=capture_output, encoding="UTF-8", cwd=cwd) + if cmd_process.returncode == 0: + if cmd_process.stdout: + return cmd_process.stdout + else: + return "" + else: + print(f"ERROR: {name} failed.") + if cmd_process.stdout: + print(cmd_process.stdout) + if cmd_process.stderr: + print(cmd_process.stderr) + exit(errcode) + return "" + + +def download_archives(base_url: str, archives: Callable[[str], Iterator[str]], version: str, dst_dir: pathlib.Path): + """Download archives from the given base_url. + + Archives is a generator for Blender archive names based on version. + + Archive names are appended to the base_url to load from, and appended to + dst_dir to save to. + + Args: + base_url (str): Base URL to load archives from + archives (Callable[[str], Iterator[str]]): Generator for Blender archive + names based on version + version (str): Version string in the form of 2.83.2 + dst_dir (pathlib.Path): Download destination + """ + + if base_url[-1] != '/': + base_url = base_url + '/' + + for archive in archives(version): + download_url = f"{base_url}{archive}" + target_file = dst_dir.joinpath(archive) + download_file(download_url, target_file) + + +def download_file(from_url: str, to_file: pathlib.Path) -> None: + """Download from_url as to_file. + + Actual downloading will be skipped if --skipdl is given on the command-line. + + Args: + from_url (str): Full URL to resource to download + to_file (pathlib.Path): Full path to save downloaded resource as + """ + global args + + if not args.skipdl or not to_file.exists(): + print(f"Downloading {from_url}") + with open(to_file, "wb") as download_zip: + response = requests.get(from_url) + if response.status_code != requests.codes.ok: + print(f"ERROR: failed to download {from_url} (status code: {response.status_code})") + exit(1313) + download_zip.write(response.content) + else: + print(f"Downloading {from_url} skipped") + print(" ... OK") + + +def copy_contents_from_dmg_to_path(dmg_file: pathlib.Path, dst: pathlib.Path) -> None: + """Copy the contents of the given DMG file to the destination folder. + + Args: + dmg_file (pathlib.Path): Full path to DMG archive to extract from + dst (pathlib.Path): Full path to destination to extract to + """ + hdiutil_attach = ["hdiutil", + "attach", + "-readonly", + f"{dmg_file}" + ] + attached = execute_command(hdiutil_attach, "hdiutil attach", 1) + + # Last line of output is what we want, it is of the form + # /dev/somedisk Apple_HFS /Volumes/Blender + # We want to retain the mount point, and the folder the mount is + # created on. The mounted disk we need for detaching, the folder we + # need to be able to copy the contents to where we can use them + attachment_items = attached.splitlines()[-1].split() + mounted_disk = attachment_items[0] + source_location = pathlib.Path(attachment_items[2], "Blender.app") + + print(f"{source_location} -> {dst}") + + shutil.copytree(source_location, dst) + + hdiutil_detach = ["hdiutil", + "detach", + f"{mounted_disk}" + ] + execute_command(hdiutil_detach, "hdiutil detach", 2) + + +def create_build_script(template_name: str, vars: List[Tuple[str, str]]) -> pathlib.Path: + """ + Create the Steam build script + + Use the given template and template variable tuple list. + + Returns pathlib.Path to the created script. + + Args: + template_name (str): [description] + vars (List[Tuple[str, str]]): [description] + + Returns: + pathlib.Path: Full path to the generated script + """ + build_script = pathlib.Path(".", template_name).read_text() + for var in vars: + build_script = build_script.replace(var[0], var[1]) + build_script_file = template_name.replace(".template", "") + build_script_path = pathlib.Path(".", build_script_file) + build_script_path.write_text(build_script) + return build_script_path + + +def clean_up() -> None: + """Remove intermediate files depending on given command-line arguments + """ + global content_location, args + + if not args.leavearch and not args.leaveextracted: + shutil.rmtree(content_location) + + if args.leavearch and not args.leaveextracted: + shutil.rmtree(content_location.joinpath(zip_extract_folder)) + shutil.rmtree(content_location.joinpath(tarxz_extract_folder)) + shutil.rmtree(content_location.joinpath(dmg_extract_folder)) + + if args.leaveextracted and not args.leavearch: + import os + os.remove(content_location.joinpath(zipped_blender)) + os.remove(content_location.joinpath(tarxz_blender)) + os.remove(content_location.joinpath(dmg_blender)) + + +def extract_archive(archive: str, extract_folder_name: str, + cmd: List[str], errcode: int) -> None: + """Extract all files from archive to given folder name. + + Will not extract if + target folder already exists, or if --skipextract was given on the + command-line. + + Args: + archive (str): Archive name to extract + extract_folder_name (str): Folder name to extract to + cmd (List[str]): Command with arguments to use + errcode (int): Error code to use for exit() + """ + global args, content_location + + extract_location = content_location.joinpath(extract_folder_name) + + pre_extract = set(content_location.glob("*")) + + if not args.skipextract or not extract_location.exists(): + print(f"Extracting files from {archive}...") + cmd.append(content_location.joinpath(archive)) + execute_command(cmd, cmd[0], errcode, cwd=content_location) + # in case we use a non-release archive the naming will be incorrect. + # simply rename to expected target name + post_extract = set(content_location.glob("*")) + diff_extract = post_extract - pre_extract + if not extract_location in diff_extract: + folder_to_rename = list(diff_extract)[0] + folder_to_rename.rename(extract_location) + print(" OK") + else: + print(f"Skipping extraction {archive}!") + +# ============================================================================== + + +parser = argparse.ArgumentParser() + +parser.add_argument("--baseurl", required=True, + help="The base URL for files to download, " + "i.e. https://download.blender.org/release/Blender2.83/") + +parser.add_argument("--version", required=True, + help="The Blender version to release, in the form 2.83.3") + +parser.add_argument("--appid", required=True, + help="The Blender App ID on Steam") +parser.add_argument("--winid", required=True, + help="The Windows depot ID") +parser.add_argument("--linuxid", required=True, + help="The Linux depot ID") +parser.add_argument("--macosid", required=True, + help="The MacOS depot ID") + +parser.add_argument("--steamcmd", required=True, + help="Path to the steamcmd") +parser.add_argument("--steamuser", required=True, + help="The login for the Steam builder user") +parser.add_argument("--steampw", required=True, + help="Login password for the Steam builder user") + +add_optional_argument("--dryrun", + "If set the Steam files will not be uploaded") +add_optional_argument("--leavearch", + help="If set don't clean up the downloaded archives") +add_optional_argument("--leaveextracted", + help="If set don't clean up the extraction folders") +add_optional_argument("--skipdl", + help="If set downloading the archives is skipped if it already exists locally.") +add_optional_argument("--skipextract", + help="If set skips extracting of archives. The tool assumes the archives" + "have already been extracted to their correct locations") + +args = parser.parse_args() + +VERSIONNODOTS = args.version.replace('.', '') +OUTPUT = f"output{VERSIONNODOTS}" +CONTENT = f"content{VERSIONNODOTS}" + +# ===== set up main locations + +content_location = pathlib.Path(".", CONTENT).absolute() +output_location = pathlib.Path(".", OUTPUT).absolute() + +content_location.mkdir(parents=True, exist_ok=True) +output_location.mkdir(parents=True, exist_ok=True) + +# ===== login + +# Logging into Steam once to ensure the SDK updates itself properly. If we don't +# do that the combined +login and +run_app_build_http at the end of the tool +# will fail. +steam_login = [args.steamcmd, + "+login", + args.steamuser, + args.steampw, + "+quit" + ] +print("Logging in to Steam...") +execute_command(steam_login, "Login to Steam", 10) +print(" OK") + +# ===== prepare Steam build scripts + +template_vars = [ + ("[APPID]", args.appid), + ("[OUTPUT]", OUTPUT), + ("[CONTENT]", CONTENT), + ("[VERSION]", args.version), + ("[WINID]", args.winid), + ("[LINUXID]", args.linuxid), + ("[MACOSID]", args.macosid), + ("[DRYRUN]", f"{args.dryrun}" if args.dryrun else "0") +] + +blender_app_build = create_build_script( + "blender_app_build.vdf.template", template_vars) +create_build_script("depot_build_win.vdf.template", template_vars) +create_build_script("depot_build_linux.vdf.template", template_vars) +create_build_script("depot_build_macos.vdf.template", template_vars) + +# ===== download archives + +download_archives(args.baseurl, blender_archives, + args.version, content_location) + +# ===== set up file and folder names + +zipped_blender = get_archive_type("zip", args.version) +zip_extract_folder = zipped_blender.replace(".zip", "") +tarxz_blender = get_archive_type("tar.xz", args.version) +tarxz_extract_folder = tarxz_blender.replace(".tar.xz", "") +dmg_blender = get_archive_type("dmg", args.version) +dmg_extract_folder = dmg_blender.replace(".dmg", "") + +# ===== extract + +unzip_cmd = ["unzip", "-q"] +extract_archive(zipped_blender, zip_extract_folder, unzip_cmd, 3) + +untarxz_cmd = ["tar", "-xf"] +extract_archive(tarxz_blender, tarxz_extract_folder, untarxz_cmd, 4) + +if not args.skipextract or not content_location.joinpath(dmg_extract_folder).exists(): + print("Extracting files from Blender MacOS archive...") + blender_dmg = content_location.joinpath(dmg_blender) + target_location = content_location.joinpath( + dmg_extract_folder, "Blender.app") + copy_contents_from_dmg_to_path(blender_dmg, target_location) + print(" OK") +else: + print("Skipping extraction of .dmg!") + +# ===== building + +print("Build Steam game files...") +steam_build = [args.steamcmd, + "+login", + args.steamuser, + args.steampw, + "+run_app_build_http", + blender_app_build.absolute(), + "+quit" + ] +execute_command(steam_build, "Build with steamcmd", 13) +print(" OK") + +clean_up() diff --git a/release/steam/depot_build_linux.vdf.template b/release/steam/depot_build_linux.vdf.template new file mode 100644 index 00000000000..0f69008548e --- /dev/null +++ b/release/steam/depot_build_linux.vdf.template @@ -0,0 +1,31 @@ +"DepotBuildConfig" +{ + // Set your assigned depot ID here + "DepotID" "[LINUXID]" + + // Set a root for all content. + // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) + // will be resolved relative to this root. + // If you don't define ContentRoot, then it will be assumed to be + // the location of this script file, which probably isn't what you want + "ContentRoot" "./blender-[VERSION]-linux64/" + + // include all files recursivley + "FileMapping" + { + // This can be a full path, or a path relative to ContentRoot + "LocalPath" "*" + + // This is a path relative to the install folder of your game + "DepotPath" "." + + // If LocalPath contains wildcards, setting this means that all + // matching files within subdirectories of LocalPath will also + // be included. + "recursive" "1" + } + + // but exclude all symbol files + // This can be a full path, or a path relative to ContentRoot + "FileExclusion" "*.pdb" +} diff --git a/release/steam/depot_build_macos.vdf.template b/release/steam/depot_build_macos.vdf.template new file mode 100644 index 00000000000..33dde860462 --- /dev/null +++ b/release/steam/depot_build_macos.vdf.template @@ -0,0 +1,30 @@ +"DepotBuildConfig" +{ + // Set your assigned depot ID here + "DepotID" "[MACOSID]" + + // Set a root for all content. + // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) + // will be resolved relative to this root. + // If you don't define ContentRoot, then it will be assumed to be + // the location of this script file, which probably isn't what you want + "ContentRoot" "./blender-[VERSION]-macOS/" + // include all files recursivley + "FileMapping" + { + // This can be a full path, or a path relative to ContentRoot + "LocalPath" "*" + + // This is a path relative to the install folder of your game + "DepotPath" "." + + // If LocalPath contains wildcards, setting this means that all + // matching files within subdirectories of LocalPath will also + // be included. + "recursive" "1" + } + + // but exclude all symbol files + // This can be a full path, or a path relative to ContentRoot + "FileExclusion" "*.pdb" +} diff --git a/release/steam/depot_build_win.vdf.template b/release/steam/depot_build_win.vdf.template new file mode 100644 index 00000000000..2c18a0f15dd --- /dev/null +++ b/release/steam/depot_build_win.vdf.template @@ -0,0 +1,31 @@ +"DepotBuildConfig" +{ + // Set your assigned depot ID here + "DepotID" "[WINID]" + + // Set a root for all content. + // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) + // will be resolved relative to this root. + // If you don't define ContentRoot, then it will be assumed to be + // the location of this script file, which probably isn't what you want + "ContentRoot" "./blender-[VERSION]-windows64/" + + // include all files recursivley + "FileMapping" + { + // This can be a full path, or a path relative to ContentRoot + "LocalPath" "*" + + // This is a path relative to the install folder of your game + "DepotPath" "." + + // If LocalPath contains wildcards, setting this means that all + // matching files within subdirectories of LocalPath will also + // be included. + "recursive" "1" + } + + // but exclude all symbol files + // This can be a full path, or a path relative to ContentRoot + "FileExclusion" "*.pdb" +} diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c index 51d3849aa48..25235097505 100644 --- a/source/blender/blenfont/intern/blf_dir.c +++ b/source/blender/blenfont/intern/blf_dir.c @@ -47,6 +47,9 @@ #include "blf_internal.h" #include "blf_internal_types.h" +#include "BKE_global.h" +#include "BKE_main.h" + static ListBase global_font_dir = {NULL, NULL}; static DirBLF *blf_dir_find(const char *path) @@ -137,9 +140,11 @@ char *blf_dir_search(const char *file) } if (!s) { - /* check the current directory, why not ? */ - if (BLI_exists(file)) { - s = BLI_strdup(file); + /* Assume file is either an abslute path, or a relative path to current directory. */ + BLI_strncpy(full_path, file, sizeof(full_path)); + BLI_path_abs(full_path, BKE_main_blendfile_path(G_MAIN)); + if (BLI_exists(full_path)) { + s = BLI_strdup(full_path); } } diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index d8605941974..717cfa607ad 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -30,10 +30,10 @@ extern "C" { #endif -struct BlendWriter; struct BlendDataReader; -struct BlendLibReader; struct BlendExpander; +struct BlendLibReader; +struct BlendWriter; struct bArmature; /* The following structures are defined in DNA_action_types.h, and DNA_anim_types.h */ diff --git a/source/blender/blenkernel/BKE_anim_visualization.h b/source/blender/blenkernel/BKE_anim_visualization.h index decb2e0b210..4e86abeed8d 100644 --- a/source/blender/blenkernel/BKE_anim_visualization.h +++ b/source/blender/blenkernel/BKE_anim_visualization.h @@ -26,14 +26,14 @@ extern "C" { #endif +struct BlendDataReader; +struct BlendWriter; struct Object; struct ReportList; struct Scene; struct bAnimVizSettings; struct bMotionPath; struct bPoseChannel; -struct BlendWriter; -struct BlendDataReader; /* ---------------------------------------------------- */ /* Animation Visualization */ diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index e812d04c7d1..8d904bd6019 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -45,8 +45,6 @@ struct NlaKeyframingContext; struct PathResolvedRNA; struct PointerRNA; struct PropertyRNA; -struct ReportList; -struct Scene; struct bAction; struct bActionGroup; struct bContext; diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h index 20df6109c13..38cd5747343 100644 --- a/source/blender/blenkernel/BKE_asset.h +++ b/source/blender/blenkernel/BKE_asset.h @@ -27,8 +27,8 @@ extern "C" { #endif -struct BlendWriter; struct BlendDataReader; +struct BlendWriter; struct ID; struct PreviewImage; diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 55a841d8fd1..574d9904dc4 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -35,7 +35,6 @@ extern "C" { struct CustomData; struct CustomDataLayer; struct ID; -struct PointerRNA; struct ReportList; /* Attribute.domain */ diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c4a704ef385..eafd86d176b 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -26,8 +26,6 @@ #include "BLI_color.hh" #include "BLI_float3.hh" -struct Mesh; - namespace blender::bke { using fn::CPPType; @@ -266,9 +264,11 @@ template<typename T> class TypedWriteAttribute { } }; +using BooleanReadAttribute = TypedReadAttribute<bool>; using FloatReadAttribute = TypedReadAttribute<float>; using Float3ReadAttribute = TypedReadAttribute<float3>; using Color4fReadAttribute = TypedReadAttribute<Color4f>; +using BooleanWriteAttribute = TypedWriteAttribute<bool>; using FloatWriteAttribute = TypedWriteAttribute<float>; using Float3WriteAttribute = TypedWriteAttribute<float3>; using Color4fWriteAttribute = TypedWriteAttribute<Color4f>; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index afb6112b954..1ed4d1183a1 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 6 +#define BLENDER_FILE_SUBVERSION 8 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h index a10a4e3c7fd..ff1bca896b1 100644 --- a/source/blender/blenkernel/BKE_collision.h +++ b/source/blender/blenkernel/BKE_collision.h @@ -26,9 +26,9 @@ extern "C" { #endif +struct BVHTree; struct Collection; struct CollisionModifierData; -struct BVHTree; struct Depsgraph; struct MVert; struct MVertTri; diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 7a14787c191..afad1e26159 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -23,6 +23,10 @@ * \ingroup bke */ +struct BlendDataReader; +struct BlendExpander; +struct BlendLibReader; +struct BlendWriter; struct Depsgraph; struct ID; struct ListBase; @@ -31,10 +35,6 @@ struct Scene; struct bConstraint; struct bConstraintTarget; struct bPoseChannel; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; -struct BlendExpander; /* ---------------------------------------------------------------------------- */ #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h index 71b15e47203..433c25084ad 100644 --- a/source/blender/blenkernel/BKE_cryptomatte.h +++ b/source/blender/blenkernel/BKE_cryptomatte.h @@ -29,10 +29,9 @@ extern "C" { #endif -struct Object; -struct Material; -struct ID; struct Main; +struct Material; +struct Object; uint32_t BKE_cryptomatte_hash(const char *name, int name_len); uint32_t BKE_cryptomatte_object_hash(const struct Object *object); diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 0c84ad70845..2fb713a4299 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -35,9 +35,7 @@ struct BMLoop; struct BMesh; struct BoundBox; struct Depsgraph; -struct EditMeshData; struct Mesh; -struct MeshStatVis; struct Object; struct Scene; diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h index 87bb96be145..37a3ed82bb8 100644 --- a/source/blender/blenkernel/BKE_geometry_set.h +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -24,9 +24,9 @@ extern "C" { #endif -struct Object; -struct GeometrySet; struct Collection; +struct GeometrySet; +struct Object; void BKE_geometry_set_free(struct GeometrySet *geometry_set); diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 617e0c45ae4..90d444aa270 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -32,10 +32,10 @@ #include "BKE_attribute_access.hh" #include "BKE_geometry_set.h" +struct Collection; struct Mesh; -struct PointCloud; struct Object; -struct Collection; +struct PointCloud; /* Each geometry component has a specific type. The type determines what kind of data the component * stores. Functions modifying a geometry will usually just modify a subset of the component types. diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 6dc8d1ef06e..df5711f5120 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -36,19 +36,17 @@ struct ListBase; struct MDeformVert; struct Main; struct Material; -struct MaterialGPencilStyle; struct Object; struct Scene; struct SpaceImage; struct ToolSettings; struct ViewLayer; struct bDeformGroup; +struct bGPDcurve; struct bGPDframe; struct bGPDlayer; struct bGPDlayer_Mask; -struct bGPDspoint; struct bGPDstroke; -struct bGPDcurve; struct bGPdata; #define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)) diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h index 1821972469c..2d42bb36949 100644 --- a/source/blender/blenkernel/BKE_gpencil_curve.h +++ b/source/blender/blenkernel/BKE_gpencil_curve.h @@ -30,10 +30,10 @@ extern "C" { struct Main; struct Object; struct Scene; -struct bGPdata; +struct bGPDcurve; struct bGPDlayer; struct bGPDstroke; -struct bGPDcurve; +struct bGPdata; void BKE_gpencil_convert_curve(struct Main *bmain, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index 111f9030463..1c86df73d3c 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -32,12 +32,11 @@ struct Depsgraph; struct Main; struct Object; struct Scene; +struct bGPDcurve; struct bGPDframe; -struct bGPDlayer; struct bGPDspoint; struct bGPDstroke; struct bGPdata; -struct bGPDcurve; /* Object boundbox. */ bool BKE_gpencil_data_minmax(const struct bGPdata *gpd, float r_min[3], float r_max[3]); diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h index ccf65a585ef..61ccf3d60f6 100644 --- a/source/blender/blenkernel/BKE_gpencil_modifier.h +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -27,6 +27,9 @@ extern "C" { #endif struct ARegionType; +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct Depsgraph; struct GpencilModifierData; struct ID; @@ -35,9 +38,6 @@ struct Main; struct ModifierUpdateDepsgraphContext; struct Object; struct Scene; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; /* NOTE: bakeModifier() called from UI: * needs to create new data-blocks, hence the need for this. */ struct bGPDframe; diff --git a/source/blender/blenkernel/BKE_ipo.h b/source/blender/blenkernel/BKE_ipo.h index 40b9f738bfd..f4871c83caf 100644 --- a/source/blender/blenkernel/BKE_ipo.h +++ b/source/blender/blenkernel/BKE_ipo.h @@ -26,7 +26,6 @@ extern "C" { #endif -struct Ipo; struct Main; void do_versions_ipos_to_animato(struct Main *main); diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h index f4c1a6fdcb4..02fa8b306d3 100644 --- a/source/blender/blenkernel/BKE_lattice.h +++ b/source/blender/blenkernel/BKE_lattice.h @@ -38,7 +38,6 @@ struct Main; struct Mesh; struct Object; struct Scene; -struct bGPDstroke; void BKE_lattice_resize(struct Lattice *lt, int u, int v, int w, struct Object *ltOb); struct Lattice *BKE_lattice_add(struct Main *bmain, const char *name); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 9f3536c9314..b0971278dc7 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -51,7 +51,6 @@ extern "C" { #endif -struct BlendDataReader; struct BlendWriter; struct GHash; struct ID; diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index bf6b5cbccef..13edabd4cb7 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -72,6 +72,7 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain, void BKE_lib_override_library_override_group_tag(struct Main *bmain, struct ID *id_root, const uint tag, + const uint missing_tag, const bool do_create_main_relashionships); bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index fb14e340d8b..a8d75213d39 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -38,8 +38,6 @@ extern "C" { #endif -struct wmWindowManager; - /* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */ /* Also IDRemap->flag. */ diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 0f34549a3cd..2d8dc852d7c 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -26,7 +26,6 @@ #include "BLI_utildefines.h" struct BLI_Stack; -struct BMEditMesh; struct BMesh; struct BMeshCreateParams; struct BMeshFromMeshParams; diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h new file mode 100644 index 00000000000..2d5c85d4129 --- /dev/null +++ b/source/blender/blenkernel/BKE_mesh_fair.h @@ -0,0 +1,56 @@ +/* + * 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. + * + * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing": + * https://github.com/fedackb/mesh-fairing. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#include "BLI_utildefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mesh Fairing. */ +/* Creates a smooth as possible geometry patch in a defined area. Different values of depth allow + * to minimize changes in the vertex positions or tangency in the affected area. */ + +typedef enum eMeshFairingDepth { + MESH_FAIRING_DEPTH_POSITION = 1, + MESH_FAIRING_DEPTH_TANGENCY = 2, +} eMeshFairingDepth; + +/* affect_vertices is used to define the fairing area. Indexed by vertex index, set to true when + * the vertex should be modified by fairing. */ +void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm, + bool *affect_vertices, + const eMeshFairingDepth depth); + +/* This function can optionally use the MVert coordinates of deform_mverts to read and write the + * fairing result. When NULL, the function will use mesh->mverts directly. */ +void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh, + struct MVert *deform_mverts, + bool *affect_vertices, + const eMeshFairingDepth depth); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 38b449cd92d..685a8ed98e2 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -35,6 +35,7 @@ struct BlendWriter; struct CustomData_MeshMasks; struct DepsNodeHandle; struct Depsgraph; +struct GeometrySet; struct ID; struct ListBase; struct Main; @@ -43,7 +44,6 @@ struct ModifierData; struct Object; struct Scene; struct bArmature; -struct GeometrySet; typedef enum { /* Should not be used, only for None modifier type */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 77a1d3989b0..5f69fc397e8 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1347,11 +1347,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_POINT_INSTANCE 1005 #define GEO_NODE_SUBDIVISION_SURFACE 1006 #define GEO_NODE_OBJECT_INFO 1007 -#define GEO_NODE_RANDOM_ATTRIBUTE 1008 +#define GEO_NODE_ATTRIBUTE_RANDOMIZE 1008 #define GEO_NODE_ATTRIBUTE_MATH 1009 #define GEO_NODE_JOIN_GEOMETRY 1010 #define GEO_NODE_ATTRIBUTE_FILL 1011 #define GEO_NODE_ATTRIBUTE_MIX 1012 +#define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013 /** \} */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 0a50dde1874..aaed2649ad9 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -34,6 +34,9 @@ extern "C" { struct BMFace; struct BMesh; +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct Brush; struct CurveMapping; struct Depsgraph; @@ -55,7 +58,6 @@ struct Paint; struct PaintCurve; struct Palette; struct PaletteColor; -struct ReportList; struct Scene; struct StrokeCache; struct SubdivCCG; @@ -67,9 +69,6 @@ struct ViewLayer; struct bContext; struct bToolRef; struct tPaletteColorHSV; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; extern const char PAINT_CURSOR_SCULPT[3]; extern const char PAINT_CURSOR_VERTEX_PAINT[3]; diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 2ac39be7eb3..3913ede9049 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -42,9 +42,9 @@ struct ParticleSystemModifierData; struct BVHTreeRay; struct BVHTreeRayHit; -struct BlendWriter; struct BlendDataReader; struct BlendLibReader; +struct BlendWriter; struct CustomData_MeshMasks; struct Depsgraph; struct EdgeHash; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index fd600a41796..0fa44067b16 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -48,7 +48,6 @@ struct PBVH; struct PBVHNode; struct SubdivCCG; struct TaskParallelSettings; -struct TaskParallelTLS; typedef struct PBVH PBVH; typedef struct PBVHNode PBVHNode; diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index d3ae043588d..170eb4ba662 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -77,7 +77,10 @@ extern "C" { #define PTCACHE_READ_OLD 3 /* Structs */ +struct BlendDataReader; +struct BlendWriter; struct ClothModifierData; +struct DynamicPaintSurface; struct FluidModifierData; struct ListBase; struct Main; @@ -89,9 +92,6 @@ struct RigidBodyWorld; struct Scene; struct SoftBody; struct ViewLayer; -struct BlendWriter; -struct BlendDataReader; -struct DynamicPaintSurface; /* temp structure for read/write */ typedef struct PTCacheData { diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 473a684eaba..7b5df98d148 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -51,7 +51,6 @@ struct View3D; struct View3DShading; struct WorkSpace; struct bContext; -struct bContextDataResult; struct bScreen; struct uiLayout; struct uiList; diff --git a/source/blender/blenkernel/BKE_shader_fx.h b/source/blender/blenkernel/BKE_shader_fx.h index b5b5f61a6a6..23bd62c70bc 100644 --- a/source/blender/blenkernel/BKE_shader_fx.h +++ b/source/blender/blenkernel/BKE_shader_fx.h @@ -27,14 +27,14 @@ extern "C" { #endif struct ARegionType; +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct ID; struct ListBase; struct ModifierUpdateDepsgraphContext; struct Object; struct ShaderFxData; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; #define SHADER_FX_ACTIVE(_fx, _is_render) \ ((((_fx)->mode & eShaderFxMode_Realtime) && (_is_render == false)) || \ diff --git a/source/blender/blenkernel/BKE_speaker.h b/source/blender/blenkernel/BKE_speaker.h index e288c9f3eb4..9defa887d3c 100644 --- a/source/blender/blenkernel/BKE_speaker.h +++ b/source/blender/blenkernel/BKE_speaker.h @@ -26,7 +26,6 @@ extern "C" { #endif struct Main; -struct Speaker; void *BKE_speaker_add(struct Main *bmain, const char *name); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 0067e1c8209..ae0a180311c 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -42,6 +42,7 @@ set(INC ../simulation ../../../intern/ghost ../../../intern/glew-mx + ../../../intern/eigen ../../../intern/guardedalloc ../../../intern/iksolver/extern ../../../intern/atomic @@ -170,6 +171,7 @@ set(SRC intern/mesh.c intern/mesh_convert.c intern/mesh_evaluate.c + intern/mesh_fair.cc intern/mesh_iterators.c intern/mesh_mapping.c intern/mesh_merge.c @@ -353,6 +355,7 @@ set(SRC BKE_mball_tessellate.h BKE_mesh.h BKE_mesh_iterators.h + BKE_mesh_fair.h BKE_mesh_mapping.h BKE_mesh_mirror.h BKE_mesh_remap.h diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index a6b3985e80d..ae0c27635a6 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -906,14 +906,20 @@ bool BKE_appdir_program_python_search(char *fullpath, const char *python_build_def = STRINGIFY(PYTHON_EXECUTABLE_NAME); #endif const char *basename = "python"; +#if defined(WIN32) && !defined(NDEBUG) + const char *basename_debug = "python_d"; +#endif char python_version[16]; /* Check both possible names. */ const char *python_names[] = { #ifdef PYTHON_EXECUTABLE_NAME - python_build_def, + python_build_def, +#endif +#if defined(WIN32) && !defined(NDEBUG) + basename_debug, #endif - python_version, - basename, + python_version, + basename, }; bool is_found = false; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 282e9bc2962..623335f65a1 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -392,6 +392,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty return &CPPType::get<int>(); case CD_PROP_COLOR: return &CPPType::get<Color4f>(); + case CD_PROP_BOOL: + return &CPPType::get<bool>(); default: return nullptr; } @@ -415,6 +417,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is<Color4f>()) { return CD_PROP_COLOR; } + if (type.is<bool>()) { + return CD_PROP_BOOL; + } return static_cast<CustomDataType>(-1); } @@ -449,6 +454,9 @@ static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom case CD_PROP_COLOR: return std::make_unique<ArrayReadAttribute<Color4f>>( domain, Span(static_cast<Color4f *>(layer.data), size)); + case CD_PROP_BOOL: + return std::make_unique<ArrayReadAttribute<bool>>( + domain, Span(static_cast<bool *>(layer.data), size)); } } } @@ -490,6 +498,9 @@ static WriteAttributePtr write_attribute_from_custom_data( case CD_PROP_COLOR: return std::make_unique<ArrayWriteAttribute<Color4f>>( domain, MutableSpan(static_cast<Color4f *>(layer.data), size)); + case CD_PROP_BOOL: + return std::make_unique<ArrayWriteAttribute<bool>>( + domain, MutableSpan(static_cast<bool *>(layer.data), size)); } } } @@ -751,6 +762,7 @@ bool PointCloudComponent::attribute_domain_with_type_supported( const AttributeDomain domain, const CustomDataType data_type) const { return domain == ATTR_DOMAIN_POINT && ELEM(data_type, + CD_PROP_BOOL, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, @@ -874,8 +886,13 @@ bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain d if (!this->attribute_domain_supported(domain)) { return false; } - return ELEM( - data_type, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR); + return ELEM(data_type, + CD_PROP_BOOL, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_INT32, + CD_PROP_COLOR); } int MeshComponent::attribute_domain_size(const AttributeDomain domain) const diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 33686cea4fc..9a954a89cad 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1834,6 +1834,14 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_SPACE_ATTEN; br->curve_preset = BRUSH_CURVE_SPHERE; break; + case SCULPT_TOOL_DISPLACEMENT_SMEAR: + br->alpha = 1.0f; + br->spacing = 5; + br->hardness = 0.7f; + br->flag &= ~BRUSH_ALPHA_PRESSURE; + br->flag &= ~BRUSH_SPACE_ATTEN; + br->curve_preset = BRUSH_CURVE_SMOOTHER; + break; default: break; } @@ -1898,6 +1906,7 @@ void BKE_brush_sculpt_reset(Brush *br) case SCULPT_TOOL_MASK: case SCULPT_TOOL_DRAW_FACE_SETS: case SCULPT_TOOL_DISPLACEMENT_ERASER: + case SCULPT_TOOL_DISPLACEMENT_SMEAR: br->add_col[0] = 0.75f; br->add_col[1] = 0.75f; br->add_col[2] = 0.75f; diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index fdb3e246382..1e2bc570c65 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1837,6 +1837,21 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { layerMultiply_propfloat2, NULL, layerAdd_propfloat2}, + /* 50: CD_PROP_POOL */ + {sizeof(bool), + "bool", + 1, + N_("Boolean"), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, }; static const char *LAYERTYPENAMES[CD_NUMTYPES] = { @@ -1892,6 +1907,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDPropCol", "CDPropFloat3", "CDPropFloat2", + "CDPropBoolean", }; const CustomData_MeshMasks CD_MASK_BAREMESH = { diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 48f5e11778c..fbf69357478 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -78,7 +78,7 @@ enum { static CLG_LogRef LOG = {"bke.icons"}; /* Protected by gIconMutex. */ -static GHash *gIcons = NULL; +static GHash *gIcons = nullptr; /* Protected by gIconMutex. */ static int gNextIconId = 1; @@ -89,7 +89,7 @@ static int gFirstIconId = 1; std::mutex gIconMutex; /* Not mutex-protected! */ -static GHash *gCachedPreviews = NULL; +static GHash *gCachedPreviews = nullptr; /* Queue of icons for deferred deletion. */ typedef struct DeferredIconDeleteNode { @@ -149,7 +149,7 @@ static void icon_free_data(int icon_id, Icon *icon) } else if (icon->obj_type == ICON_DATA_STUDIOLIGHT) { StudioLight *sl = (StudioLight *)icon->obj; - if (sl != NULL) { + if (sl != nullptr) { BKE_studiolight_unset_icon_id(sl, icon_id); } } @@ -166,7 +166,7 @@ static Icon *icon_ghash_lookup(int icon_id) /* create an id for a new icon and make sure that ids from deleted icons get reused * after the integer number range is used up */ -static int get_next_free_id(void) +static int get_next_free_id() { std::scoped_lock lock(gIconMutex); int startId = gFirstIconId; @@ -214,13 +214,13 @@ void BKE_icons_free(void) BLI_assert(BLI_thread_is_main()); if (gIcons) { - BLI_ghash_free(gIcons, NULL, icon_free); - gIcons = NULL; + BLI_ghash_free(gIcons, nullptr, icon_free); + gIcons = nullptr; } if (gCachedPreviews) { BLI_ghash_free(gCachedPreviews, MEM_freeN, BKE_previewimg_freefunc); - gCachedPreviews = NULL; + gCachedPreviews = nullptr; } BLI_linklist_lockfree_free(&g_icon_delete_queue, MEM_freeN); @@ -232,9 +232,9 @@ void BKE_icons_deferred_free(void) for (DeferredIconDeleteNode *node = (DeferredIconDeleteNode *)BLI_linklist_lockfree_begin(&g_icon_delete_queue); - node != NULL; + node != nullptr; node = node->next) { - BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), NULL, icon_free); + BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), nullptr, icon_free); } BLI_linklist_lockfree_clear(&g_icon_delete_queue, MEM_freeN); } @@ -296,7 +296,7 @@ void BKE_previewimg_free(PreviewImage **prv) { if (prv && (*prv)) { BKE_previewimg_freefunc(*prv); - *prv = NULL; + *prv = nullptr; } } @@ -321,7 +321,7 @@ void BKE_previewimg_clear(struct PreviewImage *prv) PreviewImage *BKE_previewimg_copy(const PreviewImage *prv) { - PreviewImage *prv_img = NULL; + PreviewImage *prv_img = nullptr; if (prv) { prv_img = (PreviewImage *)MEM_dupallocN(prv); @@ -329,7 +329,7 @@ PreviewImage *BKE_previewimg_copy(const PreviewImage *prv) if (prv->rect[i]) { prv_img->rect[i] = (uint *)MEM_dupallocN(prv->rect[i]); } - prv_img->gputexture[i] = NULL; + prv_img->gputexture[i] = nullptr; } } return prv_img; @@ -345,7 +345,7 @@ void BKE_previewimg_id_copy(ID *new_id, const ID *old_id) PreviewImage **new_prv_p = BKE_previewimg_id_get_p(new_id); if (old_prv_p && *old_prv_p) { - BLI_assert(new_prv_p != NULL && ELEM(*new_prv_p, NULL, *old_prv_p)); + BLI_assert(new_prv_p != nullptr && ELEM(*new_prv_p, nullptr, *old_prv_p)); // const int new_icon_id = get_next_free_id(); // if (new_icon_id == 0) { @@ -379,13 +379,13 @@ PreviewImage **BKE_previewimg_id_get_p(const ID *id) break; } - return NULL; + return nullptr; } PreviewImage *BKE_previewimg_id_get(const ID *id) { PreviewImage **prv_p = BKE_previewimg_id_get_p(id); - return prv_p ? *prv_p : NULL; + return prv_p ? *prv_p : nullptr; } void BKE_previewimg_id_free(ID *id) @@ -401,13 +401,13 @@ PreviewImage *BKE_previewimg_id_ensure(ID *id) PreviewImage **prv_p = BKE_previewimg_id_get_p(id); if (prv_p) { - if (*prv_p == NULL) { + if (*prv_p == nullptr) { *prv_p = BKE_previewimg_create(); } return *prv_p; } - return NULL; + return nullptr; } void BKE_previewimg_id_custom_set(ID *id, const char *path) @@ -464,7 +464,7 @@ PreviewImage *BKE_previewimg_cached_ensure(const char *name) { BLI_assert(BLI_thread_is_main()); - PreviewImage *prv = NULL; + PreviewImage *prv = nullptr; void **key_p, **prv_p; if (!BLI_ghash_ensure_p_ex(gCachedPreviews, name, &key_p, &prv_p)) { @@ -488,7 +488,7 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, { BLI_assert(BLI_thread_is_main()); - PreviewImage *prv = NULL; + PreviewImage *prv = nullptr; void **prv_p; prv_p = BLI_ghash_lookup_p(gCachedPreviews, name); @@ -599,7 +599,7 @@ ImBuf *BKE_previewimg_to_imbuf(PreviewImage *prv, const int size) const unsigned int h = prv->h[size]; const unsigned int *rect = prv->rect[size]; - ImBuf *ima = NULL; + ImBuf *ima = nullptr; if (w > 0 && h > 0 && rect) { /* first allocate imbuf for copying preview into it */ @@ -627,7 +627,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) * but not doing so would causes all previews to be re-rendered after * undo which is too expensive. */ - if (prv == NULL) { + if (prv == nullptr) { return; } @@ -636,7 +636,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) if (!(U.flag & USER_SAVE_PREVIEWS)) { prv_copy.w[1] = 0; prv_copy.h[1] = 0; - prv_copy.rect[1] = NULL; + prv_copy.rect[1] = nullptr; } BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy); if (prv_copy.rect[0]) { @@ -649,7 +649,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) { - if (prv == NULL) { + if (prv == nullptr) { return; } @@ -657,7 +657,7 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) if (prv->rect[i]) { BLO_read_data_address(reader, &prv->rect[i]); } - prv->gputexture[i] = NULL; + prv->gputexture[i] = nullptr; /* For now consider previews read from file as finished to not confuse File Browser preview * loading. That could be smarter and check if there's a preview job running instead. * If the preview is tagged as changed, it needs to be updated anyway, so don't remove the tag. @@ -676,7 +676,7 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) void BKE_icon_changed(const int icon_id) { - Icon *icon = NULL; + Icon *icon = nullptr; if (!icon_id || G.background) { return; @@ -714,8 +714,8 @@ static Icon *icon_create(int icon_id, int obj_type, void *obj) new_icon->flag = 0; /* next two lines make sure image gets created */ - new_icon->drawinfo = NULL; - new_icon->drawinfo_free = NULL; + new_icon->drawinfo = nullptr; + new_icon->drawinfo_free = nullptr; { std::scoped_lock lock(gIconMutex); @@ -867,11 +867,11 @@ ImBuf *BKE_icon_imbuf_get_buffer(int icon_id) Icon *icon = icon_ghash_lookup(icon_id); if (!icon) { CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id); - return NULL; + return nullptr; } if (icon->obj_type != ICON_DATA_IMBUF) { CLOG_ERROR(&LOG, "icon ID does not refer to an imbuf icon: %d", icon_id); - return NULL; + return nullptr; } return (ImBuf *)icon->obj; @@ -881,13 +881,13 @@ Icon *BKE_icon_get(const int icon_id) { BLI_assert(BLI_thread_is_main()); - Icon *icon = NULL; + Icon *icon = nullptr; icon = icon_ghash_lookup(icon_id); if (!icon) { CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id); - return NULL; + return nullptr; } return icon; @@ -930,7 +930,7 @@ void BKE_icon_id_delete(struct ID *id) BKE_icons_deferred_free(); std::scoped_lock lock(gIconMutex); - BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), NULL, icon_free); + BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), nullptr, icon_free); } /** @@ -944,7 +944,7 @@ bool BKE_icon_delete(const int icon_id) } std::scoped_lock lock(gIconMutex); - if (Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL)) { + if (Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr)) { icon_free_data(icon_id, icon); icon_free(icon); return true; @@ -962,7 +962,7 @@ bool BKE_icon_delete_unmanaged(const int icon_id) std::scoped_lock lock(gIconMutex); - Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL); + Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr); if (icon) { if (UNLIKELY(icon->flag & ICON_FLAG_MANAGED)) { BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), icon); @@ -1041,8 +1041,8 @@ struct Icon_Geom *BKE_icon_geom_from_file(const char *filename) BLI_assert(BLI_thread_is_main()); size_t data_len; uchar *data = (uchar *)BLI_file_read_binary_as_mem(filename, 0, &data_len); - if (data == NULL) { - return NULL; + if (data == nullptr) { + return nullptr; } return BKE_icon_geom_from_memory(data, data_len); } diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index 9ed233ab34c..50138b34fa3 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -62,7 +62,7 @@ bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) return ibuf->rect_float != NULL; } } - else if (ibuf) { + if (ibuf) { if (ibuf->rect_float) { return image ? (image->alpha_mode != IMA_ALPHA_STRAIGHT) : false; } diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 8a699e31f37..fbad0d920ed 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -2054,7 +2054,7 @@ static void bke_view_layer_verify_aov_cb(void *userdata, const char *name, int UNUSED(channels), const char *UNUSED(chanid), - int UNUSED(type)) + eNodeSocketDatatype UNUSED(type)) { GHash *name_count = userdata; void **value_p; diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index d82315a0e7f..cabc80d4024 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -369,6 +369,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint tag, + const uint missing_tag, Library *override_group_lib_reference) { void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id); @@ -377,9 +378,16 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, return (id->tag & tag) != 0; } + /* Note: in case some reference ID is missing from linked data (and therefore its override uses + * a placeholder as reference), use `missing_tag` instead of `tag` for this override. */ if (override_group_lib_reference != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) && id->override_library->reference->lib == override_group_lib_reference) { - id->tag |= tag; + if (id->override_library->reference->tag & LIB_TAG_MISSING) { + id->tag |= missing_tag; + } + else { + id->tag |= tag; + } } /* This way we won't process again that ID, should we encounter it again through another @@ -397,7 +405,7 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, /* We only consider IDs from the same library. */ if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) { if (lib_override_hierarchy_recursive_tag( - bmain, *entry->id_pointer, tag, override_group_lib_reference) && + bmain, *entry->id_pointer, tag, missing_tag, override_group_lib_reference) && override_group_lib_reference == NULL) { id->tag |= tag; } @@ -430,7 +438,7 @@ void BKE_lib_override_library_dependencies_tag(Main *bmain, /* We tag all intermediary data-blocks in-between two overridden ones (e.g. if a shape-key * has a driver using an armature object's bone, we need to override the shape-key/obdata, * the objects using them, etc.) */ - lib_override_hierarchy_recursive_tag(bmain, id_root, tag, NULL); + lib_override_hierarchy_recursive_tag(bmain, id_root, tag, 0, NULL); BKE_main_relations_free(bmain); } @@ -447,6 +455,7 @@ void BKE_lib_override_library_dependencies_tag(Main *bmain, void BKE_lib_override_library_override_group_tag(Main *bmain, ID *id_root, const uint tag, + const uint missing_tag, const bool do_create_main_relashionships) { if (do_create_main_relashionships) { @@ -456,7 +465,7 @@ void BKE_lib_override_library_override_group_tag(Main *bmain, /* We tag all liboverride data-blocks from the same library as reference one, * being used by the root ID. */ lib_override_hierarchy_recursive_tag( - bmain, id_root, tag, id_root->override_library->reference->lib); + bmain, id_root, tag, missing_tag, id_root->override_library->reference->lib); BKE_main_relations_free(bmain); } @@ -492,8 +501,9 @@ static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_da } /* We tag all collections and objects for override. And we also tag all other data-blocks which - * would use one of those. */ - if (ELEM(GS(id->name), ID_OB, ID_GR)) { + * would use one of those. + * Note: missing IDs (aka placeholders) are never overridden. */ + if (ELEM(GS(id->name), ID_OB, ID_GR) && !(id->tag & LIB_TAG_MISSING)) { id->tag |= LIB_TAG_DOIT; } @@ -692,6 +702,12 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, &ob_proxy->proxy->id; ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; + /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not + * sure this is a valid state, but for now just abort the overriding process. */ + if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) { + return false; + } + /* We manually convert the proxy object into a library override, further override handling will * then be handled by `BKE_lib_override_library_create()` just as for a regular override * creation. @@ -725,7 +741,7 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag * linked reference ones to be overridden again. */ - BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_MISSING, true); GHash *linkedref_to_old_override = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); @@ -835,6 +851,12 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ } id->tag &= ~LIB_TAG_DOIT; } + /* Also cleanup old overrides that went missing in new linked data. */ + else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY(id)); + id->tag |= LIB_TAG_DOIT; + id->tag &= ~LIB_TAG_MISSING; + } } FOREACH_MAIN_ID_END; BKE_id_multi_tagged_delete(bmain); @@ -876,7 +898,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) id_root->tag |= LIB_TAG_DOIT; /* Tag all library overrides in the chains of dependencies from the given root one. */ - BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_DOIT, true); ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc new file mode 100644 index 00000000000..527288d06cf --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -0,0 +1,505 @@ +/* + * 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. + * + * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing": + * https://github.com/fedackb/mesh-fairing. + */ + +/** \file + * \ingroup bke + */ + +#include "BLI_map.hh" +#include "BLI_math.h" +#include "BLI_vector.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_mesh_fair.h" +#include "BKE_mesh_mapping.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "MEM_guardedalloc.h" +#include "eigen_capi.h" + +using blender::Map; +using blender::Vector; +using std::array; + +class VertexWeight { + public: + virtual float weight_at_index(const int index) = 0; + virtual ~VertexWeight() = default; +}; + +class LoopWeight { + public: + virtual float weight_at_index(const int index) = 0; + virtual ~LoopWeight() = default; +}; + +class FairingContext { + public: + /* Get coordinates of vertices which are adjacent to the loop with specified index. */ + virtual void adjacents_coords_from_loop(const int loop, + float r_adj_next[3], + float r_adj_prev[3]) = 0; + + /* Get the other vertex index for a loop. */ + virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) = 0; + + int vertex_count_get() + { + return totvert_; + } + + int loop_count_get() + { + return totvert_; + } + + MeshElemMap *vertex_loop_map_get(const int v) + { + return &vlmap_[v]; + } + + float *vertex_deformation_co_get(const int v) + { + return co_[v]; + } + + virtual ~FairingContext() = default; + + void fair_vertices(bool *affected, + const eMeshFairingDepth depth, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) + { + + fair_vertices_ex(affected, (int)depth, vertex_weight, loop_weight); + } + + protected: + Vector<float *> co_; + + int totvert_; + int totloop_; + + MeshElemMap *vlmap_; + int *vlmap_mem_; + + private: + void fair_setup_fairing(const int v, + const int i, + LinearSolver *solver, + float multiplier, + const int depth, + Map<int, int> &vert_col_map, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) + { + if (depth == 0) { + if (vert_col_map.contains(v)) { + const int j = vert_col_map.lookup(v); + EIG_linear_solver_matrix_add(solver, i, j, -multiplier); + return; + } + for (int j = 0; j < 3; j++) { + EIG_linear_solver_right_hand_side_add(solver, j, i, multiplier * co_[v][j]); + } + return; + } + + float w_ij_sum = 0; + const float w_i = vertex_weight->weight_at_index(v); + MeshElemMap *vlmap_elem = &vlmap_[v]; + for (int l = 0; l < vlmap_elem->count; l++) { + const int l_index = vlmap_elem->indices[l]; + const int other_vert = other_vertex_index_from_loop(l_index, v); + const float w_ij = loop_weight->weight_at_index(l_index); + w_ij_sum += w_ij; + fair_setup_fairing(other_vert, + i, + solver, + w_i * w_ij * multiplier, + depth - 1, + vert_col_map, + vertex_weight, + loop_weight); + } + fair_setup_fairing(v, + i, + solver, + -1 * w_i * w_ij_sum * multiplier, + depth - 1, + vert_col_map, + vertex_weight, + loop_weight); + } + + void fair_vertices_ex(bool *affected, + const int order, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) + { + Map<int, int> vert_col_map; + int num_affected_vertices = 0; + for (int i = 0; i < totvert_; i++) { + if (!affected[i]) { + continue; + } + vert_col_map.add(i, num_affected_vertices); + num_affected_vertices++; + } + + /* Early return, nothing to do. */ + if (num_affected_vertices == 0 || num_affected_vertices == totvert_) { + return; + } + + /* Setup fairing matrices */ + LinearSolver *solver = EIG_linear_solver_new(num_affected_vertices, num_affected_vertices, 3); + for (auto item : vert_col_map.items()) { + const int v = item.key; + const int col = item.value; + fair_setup_fairing(v, col, solver, 1.0f, order, vert_col_map, vertex_weight, loop_weight); + } + + /* Solve linear system */ + EIG_linear_solver_solve(solver); + + /* Copy the result back to the mesh */ + for (auto item : vert_col_map.items()) { + const int v = item.key; + const int col = item.value; + for (int j = 0; j < 3; j++) { + co_[v][j] = EIG_linear_solver_variable_get(solver, j, col); + } + } + + /* Free solver data */ + EIG_linear_solver_delete(solver); + } +}; + +class MeshFairingContext : public FairingContext { + public: + MeshFairingContext(Mesh *mesh, MVert *deform_mverts) + { + totvert_ = mesh->totvert; + totloop_ = mesh->totloop; + + medge_ = mesh->medge; + mpoly_ = mesh->mpoly; + mloop_ = mesh->mloop; + BKE_mesh_vert_loop_map_create(&vlmap_, + &vlmap_mem_, + mesh->mpoly, + mesh->mloop, + mesh->totvert, + mesh->totpoly, + mesh->totloop); + + /* Deformation coords. */ + co_.reserve(mesh->totvert); + if (deform_mverts) { + for (int i = 0; i < mesh->totvert; i++) { + co_[i] = deform_mverts[i].co; + } + } + else { + for (int i = 0; i < mesh->totvert; i++) { + co_[i] = mesh->mvert[i].co; + } + } + + loop_to_poly_map_.reserve(mesh->totloop); + for (int i = 0; i < mesh->totpoly; i++) { + for (int l = 0; l < mesh->mpoly[i].totloop; l++) { + loop_to_poly_map_[l + mesh->mpoly[i].loopstart] = i; + } + } + } + + ~MeshFairingContext() + { + MEM_SAFE_FREE(vlmap_); + MEM_SAFE_FREE(vlmap_mem_); + } + + virtual void adjacents_coords_from_loop(const int loop, + float r_adj_next[3], + float r_adj_prev[3]) override + { + const int vert = mloop_[loop].v; + const MPoly *p = &mpoly_[loop_to_poly_map_[loop]]; + const int corner = poly_find_loop_from_vert(p, &mloop_[p->loopstart], vert); + copy_v3_v3(r_adj_next, co_[ME_POLY_LOOP_NEXT(mloop_, p, corner)->v]); + copy_v3_v3(r_adj_prev, co_[ME_POLY_LOOP_PREV(mloop_, p, corner)->v]); + } + + virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override + { + MEdge *e = &medge_[mloop_[loop].e]; + if (e->v1 == v) { + return e->v2; + } + return e->v1; + } + + protected: + Mesh *mesh_; + MLoop *mloop_; + MPoly *mpoly_; + MEdge *medge_; + Vector<int> loop_to_poly_map_; +}; + +class BMeshFairingContext : public FairingContext { + public: + BMeshFairingContext(BMesh *bm) + { + this->bm = bm; + totvert_ = bm->totvert; + totloop_ = bm->totloop; + + BM_mesh_elem_table_ensure(bm, BM_VERT); + BM_mesh_elem_index_ensure(bm, BM_LOOP); + + /* Deformation coords. */ + co_.reserve(bm->totvert); + for (int i = 0; i < bm->totvert; i++) { + BMVert *v = BM_vert_at_index(bm, i); + co_[i] = v->co; + } + + bmloop_.reserve(bm->totloop); + vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(sizeof(MeshElemMap), bm->totvert, "bmesh loop map"); + vlmap_mem_ = (int *)MEM_malloc_arrayN(sizeof(int), bm->totloop, "bmesh loop map mempool"); + + BMVert *v; + BMLoop *l; + BMIter iter; + BMIter loop_iter; + int index_iter = 0; + + /* This initializes both the bmloop and the vlmap for bmesh in a single loop. */ + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + int loop_count = 0; + const int vert_index = BM_elem_index_get(v); + vlmap_[vert_index].indices = &vlmap_mem_[index_iter]; + BM_ITER_ELEM (l, &loop_iter, v, BM_LOOPS_OF_VERT) { + const int loop_index = BM_elem_index_get(l); + bmloop_[loop_index] = l; + vlmap_mem_[index_iter] = loop_index; + index_iter++; + loop_count++; + } + vlmap_[vert_index].count = loop_count; + } + } + + ~BMeshFairingContext() + { + MEM_SAFE_FREE(vlmap_); + MEM_SAFE_FREE(vlmap_mem_); + } + + virtual void adjacents_coords_from_loop(const int loop, + float r_adj_next[3], + float r_adj_prev[3]) override + { + copy_v3_v3(r_adj_next, bmloop_[loop]->next->v->co); + copy_v3_v3(r_adj_prev, bmloop_[loop]->prev->v->co); + } + + virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override + { + BMLoop *l = bmloop_[loop]; + BMVert *bmvert = BM_vert_at_index(bm, v); + BMVert *bm_other_vert = BM_edge_other_vert(l->e, bmvert); + return BM_elem_index_get(bm_other_vert); + } + + protected: + BMesh *bm; + Vector<BMLoop *> bmloop_; +}; + +class UniformVertexWeight : public VertexWeight { + public: + UniformVertexWeight(FairingContext *fairing_context) + { + const int totvert = fairing_context->vertex_count_get(); + vertex_weights_.reserve(totvert); + for (int i = 0; i < totvert; i++) { + const int tot_loop = fairing_context->vertex_loop_map_get(i)->count; + if (tot_loop != 0) { + vertex_weights_[i] = 1.0f / tot_loop; + } + else { + vertex_weights_[i] = FLT_MAX; + } + } + } + ~UniformVertexWeight() = default; + + float weight_at_index(const int index) override + { + return vertex_weights_[index]; + } + + private: + Vector<float> vertex_weights_; +}; + +class VoronoiVertexWeight : public VertexWeight { + + public: + VoronoiVertexWeight(FairingContext *fairing_context) + { + + const int totvert = fairing_context->vertex_count_get(); + vertex_weights_.reserve(totvert); + for (int i = 0; i < totvert; i++) { + + float area = 0.0f; + float a[3]; + copy_v3_v3(a, fairing_context->vertex_deformation_co_get(i)); + const float acute_threshold = M_PI_2; + + MeshElemMap *vlmap_elem = fairing_context->vertex_loop_map_get(i); + for (int l = 0; l < vlmap_elem->count; l++) { + const int l_index = vlmap_elem->indices[l]; + + float b[3], c[3], d[3]; + fairing_context->adjacents_coords_from_loop(l_index, b, c); + + if (angle_v3v3v3(c, fairing_context->vertex_deformation_co_get(i), b) < acute_threshold) { + calc_circumcenter(d, a, b, c); + } + else { + add_v3_v3v3(d, b, c); + mul_v3_fl(d, 0.5f); + } + + float t[3]; + add_v3_v3v3(t, a, b); + mul_v3_fl(t, 0.5f); + area += area_tri_v3(a, t, d); + + add_v3_v3v3(t, a, c); + mul_v3_fl(t, 0.5f); + area += area_tri_v3(a, d, t); + } + + vertex_weights_[i] = area != 0.0f ? 1.0f / area : 1e12; + } + } + ~VoronoiVertexWeight() = default; + + float weight_at_index(const int index) override + { + return vertex_weights_[index]; + } + + private: + Vector<float> vertex_weights_; + + void calc_circumcenter(float r[3], const float a[3], const float b[3], const float c[3]) + { + float ab[3]; + sub_v3_v3v3(ab, b, a); + + float ac[3]; + sub_v3_v3v3(ac, c, a); + + float ab_cross_ac[3]; + cross_v3_v3v3(ab_cross_ac, ab, ac); + + if (len_squared_v3(ab_cross_ac) > 0.0f) { + float d[3]; + cross_v3_v3v3(d, ab_cross_ac, ab); + mul_v3_fl(d, len_squared_v3(ac)); + + float t[3]; + cross_v3_v3v3(t, ac, ab_cross_ac); + mul_v3_fl(t, len_squared_v3(ab)); + + add_v3_v3(d, t); + + mul_v3_fl(d, 1.0f / (2.0f * len_squared_v3(ab_cross_ac))); + + add_v3_v3v3(r, a, d); + return; + } + copy_v3_v3(r, a); + } +}; + +class UniformLoopWeight : public LoopWeight { + public: + float weight_at_index(const int UNUSED(index)) override + { + return 1.0f; + } +}; + +static void prefair_and_fair_vertices(FairingContext *fairing_context, + bool *affected_vertices, + const eMeshFairingDepth depth) +{ + /* Prefair. */ + UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context); + UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight(); + fairing_context->fair_vertices( + affected_vertices, depth, uniform_vertex_weights, uniform_loop_weights); + delete uniform_vertex_weights; + + /* Fair. */ + VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context); + /* TODO: Implemente cotangent loop weights. */ + fairing_context->fair_vertices( + affected_vertices, depth, voronoi_vertex_weights, uniform_loop_weights); + + delete uniform_loop_weights; + delete voronoi_vertex_weights; +} + +void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh, + struct MVert *deform_mverts, + bool *affect_vertices, + const eMeshFairingDepth depth) +{ + MeshFairingContext *fairing_context = new MeshFairingContext(mesh, deform_mverts); + prefair_and_fair_vertices(fairing_context, affect_vertices, depth); + delete fairing_context; +} + +void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm, + bool *affect_vertices, + const eMeshFairingDepth depth) +{ + BMeshFairingContext *fairing_context = new BMeshFairingContext(bm); + prefair_and_fair_vertices(fairing_context, affect_vertices, depth); + delete fairing_context; +} diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.h b/source/blender/blenkernel/intern/multires_unsubdivide.h index 39c6da0b6c8..0a03387282f 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.h +++ b/source/blender/blenkernel/intern/multires_unsubdivide.h @@ -26,10 +26,8 @@ #include "BLI_sys_types.h" struct BMesh; -struct Depsgraph; struct Mesh; struct MultiresModifierData; -struct Object; typedef struct MultiresUnsubdivideGrid { /* For sanity checks. */ diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 87997198334..415eb14be66 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -4737,10 +4737,11 @@ static void registerGeometryNodes(void) register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); register_node_type_geo_object_info(); - register_node_type_geo_random_attribute(); + register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_math(); register_node_type_geo_join_geometry(); register_node_type_geo_attribute_mix(); + register_node_type_geo_attribute_color_ramp(); } static void registerFunctionNodes(void) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index f63d443d29f..cc192c1c3c0 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -222,6 +222,7 @@ static void scene_init_data(ID *id) /* Curve Profile */ scene->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE); + scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); for (size_t i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) { scene->orientation_slots[i].index_custom = -1; @@ -862,6 +863,9 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres if (tos->custom_bevel_profile_preset) { BKE_curveprofile_blend_write(writer, tos->custom_bevel_profile_preset); } + if (tos->sequencer_tool_settings) { + BLO_write_struct(writer, SequencerToolSettings, tos->sequencer_tool_settings); + } BKE_paint_blend_write(writer, &tos->imapaint.paint); @@ -1121,6 +1125,8 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) if (sce->toolsettings->custom_bevel_profile_preset) { BKE_curveprofile_blend_read(reader, sce->toolsettings->custom_bevel_profile_preset); } + + BLO_read_data_address(reader, &sce->toolsettings->sequencer_tool_settings); } if (sce->ed) { @@ -1792,6 +1798,8 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->gp_sculpt.cur_primitive = BKE_curvemapping_copy(ts->gp_sculpt.cur_primitive); ts->custom_bevel_profile_preset = BKE_curveprofile_copy(ts->custom_bevel_profile_preset); + + ts->sequencer_tool_settings = SEQ_tool_settings_copy(ts->sequencer_tool_settings); return ts; } @@ -1850,6 +1858,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_curveprofile_free(toolsettings->custom_bevel_profile_preset); } + if (toolsettings->sequencer_tool_settings) { + SEQ_tool_settings_free(toolsettings->sequencer_tool_settings); + } + MEM_freeN(toolsettings); } diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 8a7dcb7ffaa..284d62fb876 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -220,13 +220,13 @@ class Array { return MutableSpan<T>(data_, size_); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator Span<U>() const { return Span<U>(data_, size_); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator MutableSpan<U>() { return MutableSpan<U>(data_, size_); diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh index 2b060c986cd..4121542c887 100644 --- a/source/blender/blenlib/BLI_index_range.hh +++ b/source/blender/blenlib/BLI_index_range.hh @@ -73,21 +73,22 @@ class IndexRange { int64_t size_ = 0; public: - IndexRange() = default; + constexpr IndexRange() = default; - explicit IndexRange(int64_t size) : start_(0), size_(size) + constexpr explicit IndexRange(int64_t size) : start_(0), size_(size) { BLI_assert(size >= 0); } - IndexRange(int64_t start, int64_t size) : start_(start), size_(size) + constexpr IndexRange(int64_t start, int64_t size) : start_(start), size_(size) { BLI_assert(start >= 0); BLI_assert(size >= 0); } template<typename T> - IndexRange(const tbb::blocked_range<T> &range) : start_(range.begin()), size_(range.size()) + constexpr IndexRange(const tbb::blocked_range<T> &range) + : start_(range.begin()), size_(range.size()) { } @@ -96,33 +97,33 @@ class IndexRange { int64_t current_; public: - Iterator(int64_t current) : current_(current) + constexpr Iterator(int64_t current) : current_(current) { } - Iterator &operator++() + constexpr Iterator &operator++() { current_++; return *this; } - bool operator!=(const Iterator &iterator) const + constexpr bool operator!=(const Iterator &iterator) const { return current_ != iterator.current_; } - int64_t operator*() const + constexpr int64_t operator*() const { return current_; } }; - Iterator begin() const + constexpr Iterator begin() const { return Iterator(start_); } - Iterator end() const + constexpr Iterator end() const { return Iterator(start_ + size_); } @@ -130,7 +131,7 @@ class IndexRange { /** * Access an element in the range. */ - int64_t operator[](int64_t index) const + constexpr int64_t operator[](int64_t index) const { BLI_assert(index >= 0); BLI_assert(index < this->size()); @@ -140,7 +141,7 @@ class IndexRange { /** * Two ranges compare equal when they contain the same numbers. */ - friend bool operator==(IndexRange a, IndexRange b) + constexpr friend bool operator==(IndexRange a, IndexRange b) { return (a.size_ == b.size_) && (a.start_ == b.start_ || a.size_ == 0); } @@ -148,7 +149,7 @@ class IndexRange { /** * Get the amount of numbers in the range. */ - int64_t size() const + constexpr int64_t size() const { return size_; } @@ -156,7 +157,7 @@ class IndexRange { /** * Create a new range starting at the end of the current one. */ - IndexRange after(int64_t n) const + constexpr IndexRange after(int64_t n) const { BLI_assert(n >= 0); return IndexRange(start_ + size_, n); @@ -165,7 +166,7 @@ class IndexRange { /** * Create a new range that ends at the start of the current one. */ - IndexRange before(int64_t n) const + constexpr IndexRange before(int64_t n) const { BLI_assert(n >= 0); return IndexRange(start_ - n, n); @@ -175,7 +176,7 @@ class IndexRange { * Get the first element in the range. * Asserts when the range is empty. */ - int64_t first() const + constexpr int64_t first() const { BLI_assert(this->size() > 0); return start_; @@ -185,7 +186,7 @@ class IndexRange { * Get the last element in the range. * Asserts when the range is empty. */ - int64_t last() const + constexpr int64_t last() const { BLI_assert(this->size() > 0); return start_ + size_ - 1; @@ -194,7 +195,7 @@ class IndexRange { /** * Get the element one after the end. The returned value is undefined when the range is empty. */ - int64_t one_after_last() const + constexpr int64_t one_after_last() const { return start_ + size_; } @@ -202,7 +203,7 @@ class IndexRange { /** * Get the first element in the range. The returned value is undefined when the range is empty. */ - int64_t start() const + constexpr int64_t start() const { return start_; } @@ -210,7 +211,7 @@ class IndexRange { /** * Returns true when the range contains a certain number, otherwise false. */ - bool contains(int64_t value) const + constexpr bool contains(int64_t value) const { return value >= start_ && value < start_ + size_; } @@ -218,7 +219,7 @@ class IndexRange { /** * Returns a new range, that contains a sub-interval of the current one. */ - IndexRange slice(int64_t start, int64_t size) const + constexpr IndexRange slice(int64_t start, int64_t size) const { BLI_assert(start >= 0); BLI_assert(size >= 0); @@ -226,7 +227,7 @@ class IndexRange { BLI_assert(new_start + size <= start_ + size_ || size == 0); return IndexRange(new_start, size); } - IndexRange slice(IndexRange range) const + constexpr IndexRange slice(IndexRange range) const { return this->slice(range.start(), range.size()); } diff --git a/source/blender/blenlib/BLI_inplace_priority_queue.hh b/source/blender/blenlib/BLI_inplace_priority_queue.hh new file mode 100644 index 00000000000..e76cb8504a3 --- /dev/null +++ b/source/blender/blenlib/BLI_inplace_priority_queue.hh @@ -0,0 +1,304 @@ +/* + * 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. + */ + +#pragma once + +#include "BLI_array.hh" +#include "BLI_dot_export.hh" + +namespace blender { + +/** + * An InplacePriorityQueue adds priority queue functionality to an existing array. The underlying + * array is not changed. Instead, the priority queue maintains indices into the original array. + * + * The priority queue provides efficient access to the element in order of their priorities. + * + * When a priority changes, the priority queue has to be informed using one of the following + * methods: #priority_decreased, #priority_increased or #priority_changed. + */ +template< + /* Type of the elements in the underlying array. */ + typename T, + /* Binary function that takes two `const T &` inputs and returns true, when the first input has + greater priority than the second. */ + typename FirstHasHigherPriority = std::greater<T>> +class InplacePriorityQueue { + private: + /* Underlying array the priority queue is built upon. This is a span instead of a mutable span, + * because this data structure never changes the values itself. */ + Span<T> data_; + /* Maps indices from the heap (binary tree in array format) to indices of the underlying/original + * array. */ + Array<int64_t> heap_to_orig_; + /* This is the inversion of the above mapping. */ + Array<int64_t> orig_to_heap_; + /* Number of elements that are currently in the priority queue. */ + int64_t heap_size_ = 0; + /* Function that can be changed to customize how the priority of two elements is compared. */ + FirstHasHigherPriority first_has_higher_priority_fn_; + + public: + /** + * Construct the priority queue on top of the data in the given span. + */ + InplacePriorityQueue(Span<T> data) + : data_(data), heap_to_orig_(data_.size()), orig_to_heap_(data_.size()) + { + for (const int64_t i : IndexRange(data_.size())) { + heap_to_orig_[i] = i; + orig_to_heap_[i] = i; + } + + this->rebuild(); + } + + /** + * Rebuilds the priority queue from the array that has been passed to the constructor. + */ + void rebuild() + { + const int final_heap_size = data_.size(); + if (final_heap_size > 1) { + for (int64_t i = this->get_parent(final_heap_size - 1); i >= 0; i--) { + this->heapify(i, final_heap_size); + } + } + heap_size_ = final_heap_size; + } + + /** + * Returns the number of elements in the priority queue. + * This is less or equal than the size of the underlying array. + */ + int64_t size() const + { + return heap_size_; + } + + /** + * Returns true, when the priority queue contains no elements. If this returns true, #peek and + * #pop must not be used. + */ + bool is_empty() const + { + return heap_size_ == 0; + } + + /** + * Get the element with the highest priority in the priority queue. + * The returned reference is const, because the priority queue has read-only access to the + * underlying data. If you need a mutable reference, use #peek_index instead. + */ + const T &peek() const + { + return data_[this->peek_index()]; + } + + /** + * Get the element with the highest priority in the priority queue and remove it. + * The returned reference is const, because the priority queue has read-only access to the + * underlying data. If you need a mutable reference, use #pop_index instead. + */ + const T &pop() + { + return data_[this->pop_index()]; + } + + /** + * Get the index of the element with the highest priority in the priority queue. + */ + int64_t peek_index() const + { + BLI_assert(!this->is_empty()); + return heap_to_orig_[0]; + } + + /** + * Get the index of the element with the highest priority in the priority queue and remove it. + */ + int64_t pop_index() + { + BLI_assert(!this->is_empty()); + const int64_t top_index_orig = heap_to_orig_[0]; + heap_size_--; + if (heap_size_ > 1) { + this->swap_indices(0, heap_size_); + this->heapify(0, heap_size_); + } + return top_index_orig; + } + + /** + * Inform the priority queue that the priority of the element at the given index has been + * decreased. + */ + void priority_decreased(const int64_t index) + { + const int64_t heap_index = orig_to_heap_[index]; + if (heap_index >= heap_size_) { + /* This element is not in the queue currently. */ + return; + } + this->heapify(heap_index, heap_size_); + } + + /** + * Inform the priority queue that the priority of the element at the given index has been + * increased. + */ + void priority_increased(const int64_t index) + { + int64_t current = orig_to_heap_[index]; + if (current >= heap_size_) { + /* This element is not in the queue currently. */ + return; + } + while (true) { + if (current == 0) { + break; + } + const int64_t parent = this->get_parent(current); + if (this->first_has_higher_priority(parent, current)) { + break; + } + this->swap_indices(current, parent); + current = parent; + } + } + + /** + * Inform the priority queue that the priority of the element at the given index has been + * changed. + */ + void priority_changed(const int64_t index) + { + this->priority_increased(index); + this->priority_decreased(index); + } + + /** + * Returns the indices of all elements that are in the priority queue. + * There are no guarantees about the order of indices. + */ + Span<int64_t> active_indices() const + { + return heap_to_orig_.as_span().take_front(heap_size_); + } + + /** + * Returns the indices of all elements that are not in the priority queue. + * The indices are in reverse order of their removal from the queue. + * I.e. the index that has been removed last, comes first. + */ + Span<int64_t> inactive_indices() const + { + return heap_to_orig_.as_span().drop_front(heap_size_); + } + + /** + * Returns the concatenation of the active and inactive indices. + */ + Span<int64_t> all_indices() const + { + return heap_to_orig_; + } + + /** + * Return the heap used by the priority queue as dot graph string. + * This exists for debugging purposes. + */ + std::string to_dot() const + { + return this->partial_to_dot(heap_size_); + } + + private: + bool first_has_higher_priority(const int64_t a, const int64_t b) + { + const T &value_a = data_[heap_to_orig_[a]]; + const T &value_b = data_[heap_to_orig_[b]]; + return first_has_higher_priority_fn_(value_a, value_b); + } + + void swap_indices(const int64_t a, const int64_t b) + { + std::swap(heap_to_orig_[a], heap_to_orig_[b]); + orig_to_heap_[heap_to_orig_[a]] = a; + orig_to_heap_[heap_to_orig_[b]] = b; + } + + void heapify(const int64_t parent, const int64_t heap_size) + { + int64_t max_index = parent; + const int left = this->get_left(parent); + const int right = this->get_right(parent); + if (left < heap_size && this->first_has_higher_priority(left, max_index)) { + max_index = left; + } + if (right < heap_size && this->first_has_higher_priority(right, max_index)) { + max_index = right; + } + if (max_index != parent) { + this->swap_indices(parent, max_index); + this->heapify(max_index, heap_size); + } + if (left < heap_size) { + BLI_assert(!this->first_has_higher_priority(left, parent)); + } + if (right < heap_size) { + BLI_assert(!this->first_has_higher_priority(right, parent)); + } + } + + int64_t get_parent(const int64_t child) const + { + BLI_assert(child > 0); + return (child - 1) / 2; + } + + int64_t get_left(const int64_t parent) const + { + return parent * 2 + 1; + } + + int64_t get_right(const int64_t parent) const + { + return parent * 2 + 2; + } + + std::string partial_to_dot(const int size) const + { + dot::DirectedGraph digraph; + Array<dot::Node *> dot_nodes(size); + for (const int i : IndexRange(size)) { + std::stringstream ss; + ss << data_[heap_to_orig_[i]]; + const std::string name = ss.str(); + dot::Node &node = digraph.new_node(name); + node.set_shape(dot::Attr_shape::Rectangle); + node.attributes.set("ordering", "out"); + dot_nodes[i] = &node; + if (i > 0) { + const int64_t parent = this->get_parent(i); + digraph.new_edge(*dot_nodes[parent], node); + } + } + return digraph.to_dot_string(); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index c34b71a60f9..5e0ea4f2a99 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -178,6 +178,7 @@ int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersec int BLI_bvhtree_get_len(const BVHTree *tree); int BLI_bvhtree_get_tree_type(const BVHTree *tree); float BLI_bvhtree_get_epsilon(const BVHTree *tree); +void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3]); /* find nearest node to the given coordinates * (if nearest is given it will only search nodes where diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index 49076bb1aae..b3b6855089e 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -428,6 +428,25 @@ inline constexpr bool is_convertible_pointer_v = std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>; /** + * Helper variable that checks if a Span<From> can be converted to Span<To> safely, whereby From + * and To are pointers. Adding const and casting to a void pointer is allowed. + * Casting up and down a class hierarchy generally is not allowed, because this might change the + * pointer under some circumstances. + */ +template<typename From, typename To> +inline constexpr bool is_span_convertible_pointer_v = + /* Make sure we are working with pointers. */ + std::is_pointer_v<From> &&std::is_pointer_v<To> && + (/* No casting is necessary when both types are the same. */ + std::is_same_v<From, To> || + /* Allow adding const to the underlying type. */ + std::is_same_v<const std::remove_pointer_t<From>, std::remove_pointer_t<To>> || + /* Allow casting non-const pointers to void pointers. */ + (!std::is_const_v<std::remove_pointer_t<From>> && std::is_same_v<To, void *>) || + /* Allow casting any pointer to const void pointers. */ + std::is_same_v<To, const void *>); + +/** * Inline buffers for small-object-optimization should be disable by default. Otherwise we might * get large unexpected allocations on the stack. */ diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 5b4d2769f57..8011b2f9abc 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -93,15 +93,15 @@ template<typename T> class Span { /** * Create a reference to an empty array. */ - Span() = default; + constexpr Span() = default; - Span(const T *start, int64_t size) : data_(start), size_(size) + constexpr Span(const T *start, int64_t size) : data_(start), size_(size) { BLI_assert(size >= 0); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(const U *start, int64_t size) : data_(static_cast<const T *>(start)), size_(size) + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr> + constexpr Span(const U *start, int64_t size) : data_(static_cast<const T *>(start)), size_(size) { BLI_assert(size >= 0); } @@ -117,16 +117,17 @@ template<typename T> class Span { * Span<int> span = {1, 2, 3, 4}; * call_function_with_array(span); */ - Span(const std::initializer_list<T> &list) + constexpr Span(const std::initializer_list<T> &list) : Span(list.begin(), static_cast<int64_t>(list.size())) { } - Span(const std::vector<T> &vector) : Span(vector.data(), static_cast<int64_t>(vector.size())) + constexpr Span(const std::vector<T> &vector) + : Span(vector.data(), static_cast<int64_t>(vector.size())) { } - template<std::size_t N> Span(const std::array<T, N> &array) : Span(array.data(), N) + template<std::size_t N> constexpr Span(const std::array<T, N> &array) : Span(array.data(), N) { } @@ -134,8 +135,9 @@ template<typename T> class Span { * Support implicit conversions like the ones below: * Span<T *> -> Span<const T *> */ - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(Span<U> array) : data_(static_cast<const T *>(array.data())), size_(array.size()) + + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr> + constexpr Span(Span<U> array) : data_(static_cast<const T *>(array.data())), size_(array.size()) { } @@ -143,7 +145,7 @@ template<typename T> class Span { * Returns a contiguous part of the array. This invokes undefined behavior when the slice does * not stay within the bounds of the array. */ - Span slice(int64_t start, int64_t size) const + constexpr Span slice(int64_t start, int64_t size) const { BLI_assert(start >= 0); BLI_assert(size >= 0); @@ -151,7 +153,7 @@ template<typename T> class Span { return Span(data_ + start, size); } - Span slice(IndexRange range) const + constexpr Span slice(IndexRange range) const { return this->slice(range.start(), range.size()); } @@ -160,7 +162,7 @@ template<typename T> class Span { * Returns a new Span with n elements removed from the beginning. This invokes undefined * behavior when the array is too small. */ - Span drop_front(int64_t n) const + constexpr Span drop_front(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -171,7 +173,7 @@ template<typename T> class Span { * Returns a new Span with n elements removed from the beginning. This invokes undefined * behavior when the array is too small. */ - Span drop_back(int64_t n) const + constexpr Span drop_back(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -182,7 +184,7 @@ template<typename T> class Span { * Returns a new Span that only contains the first n elements. This invokes undefined * behavior when the array is too small. */ - Span take_front(int64_t n) const + constexpr Span take_front(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -193,7 +195,7 @@ template<typename T> class Span { * Returns a new Span that only contains the last n elements. This invokes undefined * behavior when the array is too small. */ - Span take_back(int64_t n) const + constexpr Span take_back(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -204,25 +206,25 @@ template<typename T> class Span { * Returns the pointer to the beginning of the referenced array. This may be nullptr when the * size is zero. */ - const T *data() const + constexpr const T *data() const { return data_; } - const T *begin() const + constexpr const T *begin() const { return data_; } - const T *end() const + constexpr const T *end() const { return data_ + size_; } - std::reverse_iterator<const T *> rbegin() const + constexpr std::reverse_iterator<const T *> rbegin() const { return std::reverse_iterator<const T *>(this->end()); } - std::reverse_iterator<const T *> rend() const + constexpr std::reverse_iterator<const T *> rend() const { return std::reverse_iterator<const T *>(this->begin()); } @@ -231,7 +233,7 @@ template<typename T> class Span { * Access an element in the array. This invokes undefined behavior when the index is out of * bounds. */ - const T &operator[](int64_t index) const + constexpr const T &operator[](int64_t index) const { BLI_assert(index >= 0); BLI_assert(index < size_); @@ -241,7 +243,7 @@ template<typename T> class Span { /** * Returns the number of elements in the referenced array. */ - int64_t size() const + constexpr int64_t size() const { return size_; } @@ -249,7 +251,7 @@ template<typename T> class Span { /** * Returns true if the size is zero. */ - bool is_empty() const + constexpr bool is_empty() const { return size_ == 0; } @@ -257,7 +259,7 @@ template<typename T> class Span { /** * Returns the number of bytes referenced by this Span. */ - int64_t size_in_bytes() const + constexpr int64_t size_in_bytes() const { return sizeof(T) * size_; } @@ -266,7 +268,7 @@ template<typename T> class Span { * Does a linear search to see of the value is in the array. * Returns true if it is, otherwise false. */ - bool contains(const T &value) const + constexpr bool contains(const T &value) const { for (const T &element : *this) { if (element == value) { @@ -280,7 +282,7 @@ template<typename T> class Span { * Does a constant time check to see if the pointer points to a value in the referenced array. * Return true if it is, otherwise false. */ - bool contains_ptr(const T *ptr) const + constexpr bool contains_ptr(const T *ptr) const { return (this->begin() <= ptr) && (ptr < this->end()); } @@ -289,7 +291,7 @@ template<typename T> class Span { * Does a linear search to count how often the value is in the array. * Returns the number of occurrences. */ - int64_t count(const T &value) const + constexpr int64_t count(const T &value) const { int64_t counter = 0; for (const T &element : *this) { @@ -304,7 +306,7 @@ template<typename T> class Span { * Return a reference to the first element in the array. This invokes undefined behavior when the * array is empty. */ - const T &first() const + constexpr const T &first() const { BLI_assert(size_ > 0); return data_[0]; @@ -314,7 +316,7 @@ template<typename T> class Span { * Returns a reference to the last element in the array. This invokes undefined behavior when the * array is empty. */ - const T &last() const + constexpr const T &last() const { BLI_assert(size_ > 0); return data_[size_ - 1]; @@ -324,7 +326,7 @@ template<typename T> class Span { * Returns the element at the given index. If the index is out of range, return the fallback * value. */ - T get(int64_t index, const T &fallback) const + constexpr T get(int64_t index, const T &fallback) const { if (index < size_ && index >= 0) { return data_[index]; @@ -336,7 +338,7 @@ template<typename T> class Span { * Check if the array contains duplicates. Does a linear search for every element. So the total * running time is O(n^2). Only use this for small arrays. */ - bool has_duplicates__linear_search() const + constexpr bool has_duplicates__linear_search() const { /* The size should really be smaller than that. If it is not, the calling code should be * changed. */ @@ -358,7 +360,7 @@ template<typename T> class Span { * called on small arrays, because it has a running time of O(n*m) where n and m are the sizes of * the arrays. */ - bool intersects__linear_search(Span other) const + constexpr bool intersects__linear_search(Span other) const { /* The size should really be smaller than that. If it is not, the calling code should be * changed. */ @@ -377,7 +379,7 @@ template<typename T> class Span { * Returns the index of the first occurrence of the given value. This invokes undefined behavior * when the value is not in the array. */ - int64_t first_index(const T &search_value) const + constexpr int64_t first_index(const T &search_value) const { const int64_t index = this->first_index_try(search_value); BLI_assert(index >= 0); @@ -387,7 +389,7 @@ template<typename T> class Span { /** * Returns the index of the first occurrence of the given value or -1 if it does not exist. */ - int64_t first_index_try(const T &search_value) const + constexpr int64_t first_index_try(const T &search_value) const { for (int64_t i = 0; i < size_; i++) { if (data_[i] == search_value) { @@ -401,7 +403,7 @@ template<typename T> class Span { * Utility to make it more convenient to iterate over all indices that can be used with this * array. */ - IndexRange index_range() const + constexpr IndexRange index_range() const { return IndexRange(size_); } @@ -409,7 +411,7 @@ template<typename T> class Span { /** * Returns a new Span to the same underlying memory buffer. No conversions are done. */ - template<typename NewT> Span<NewT> cast() const + template<typename NewT> Span<NewT> constexpr cast() const { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); @@ -450,21 +452,22 @@ template<typename T> class MutableSpan { int64_t size_; public: - MutableSpan() = default; + constexpr MutableSpan() = default; - MutableSpan(T *start, const int64_t size) : data_(start), size_(size) + constexpr MutableSpan(T *start, const int64_t size) : data_(start), size_(size) { } - MutableSpan(std::vector<T> &vector) : MutableSpan(vector.data(), vector.size()) + constexpr MutableSpan(std::vector<T> &vector) : MutableSpan(vector.data(), vector.size()) { } - template<std::size_t N> MutableSpan(std::array<T, N> &array) : MutableSpan(array.data(), N) + template<std::size_t N> + constexpr MutableSpan(std::array<T, N> &array) : MutableSpan(array.data(), N) { } - operator Span<T>() const + constexpr operator Span<T>() const { return Span<T>(data_, size_); } @@ -472,7 +475,7 @@ template<typename T> class MutableSpan { /** * Returns the number of elements in the array. */ - int64_t size() const + constexpr int64_t size() const { return size_; } @@ -480,7 +483,7 @@ template<typename T> class MutableSpan { /** * Replace all elements in the referenced array with the given value. */ - void fill(const T &value) + constexpr void fill(const T &value) { initialized_fill_n(data_, size_, value); } @@ -489,7 +492,7 @@ template<typename T> class MutableSpan { * Replace a subset of all elements with the given value. This invokes undefined behavior when * one of the indices is out of bounds. */ - void fill_indices(Span<int64_t> indices, const T &value) + constexpr void fill_indices(Span<int64_t> indices, const T &value) { for (int64_t i : indices) { BLI_assert(i < size_); @@ -501,30 +504,30 @@ template<typename T> class MutableSpan { * Returns a pointer to the beginning of the referenced array. This may be nullptr, when the size * is zero. */ - T *data() const + constexpr T *data() const { return data_; } - T *begin() const + constexpr T *begin() const { return data_; } - T *end() const + constexpr T *end() const { return data_ + size_; } - std::reverse_iterator<T *> rbegin() const + constexpr std::reverse_iterator<T *> rbegin() const { return std::reverse_iterator<T *>(this->end()); } - std::reverse_iterator<T *> rend() const + constexpr std::reverse_iterator<T *> rend() const { return std::reverse_iterator<T *>(this->begin()); } - T &operator[](const int64_t index) const + constexpr T &operator[](const int64_t index) const { BLI_assert(index < this->size()); return data_[index]; @@ -534,7 +537,7 @@ template<typename T> class MutableSpan { * Returns a contiguous part of the array. This invokes undefined behavior when the slice would * go out of bounds. */ - MutableSpan slice(const int64_t start, const int64_t length) const + constexpr MutableSpan slice(const int64_t start, const int64_t length) const { BLI_assert(start + length <= this->size()); return MutableSpan(data_ + start, length); @@ -544,7 +547,7 @@ template<typename T> class MutableSpan { * Returns a new MutableSpan with n elements removed from the beginning. This invokes * undefined behavior when the array is too small. */ - MutableSpan drop_front(const int64_t n) const + constexpr MutableSpan drop_front(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(n, this->size() - n); @@ -554,7 +557,7 @@ template<typename T> class MutableSpan { * Returns a new MutableSpan with n elements removed from the end. This invokes undefined * behavior when the array is too small. */ - MutableSpan drop_back(const int64_t n) const + constexpr MutableSpan drop_back(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(0, this->size() - n); @@ -564,7 +567,7 @@ template<typename T> class MutableSpan { * Returns a new MutableSpan that only contains the first n elements. This invokes undefined * behavior when the array is too small. */ - MutableSpan take_front(const int64_t n) const + constexpr MutableSpan take_front(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(0, n); @@ -574,7 +577,7 @@ template<typename T> class MutableSpan { * Return a new MutableSpan that only contains the last n elements. This invokes undefined * behavior when the array is too small. */ - MutableSpan take_back(const int64_t n) const + constexpr MutableSpan take_back(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(this->size() - n, n); @@ -584,7 +587,7 @@ template<typename T> class MutableSpan { * Returns an (immutable) Span that references the same array. This is usually not needed, * due to implicit conversions. However, sometimes automatic type deduction needs some help. */ - Span<T> as_span() const + constexpr Span<T> as_span() const { return Span<T>(data_, size_); } @@ -593,7 +596,7 @@ template<typename T> class MutableSpan { * Utility to make it more convenient to iterate over all indices that can be used with this * array. */ - IndexRange index_range() const + constexpr IndexRange index_range() const { return IndexRange(size_); } @@ -602,7 +605,7 @@ template<typename T> class MutableSpan { * Returns a reference to the last element. This invokes undefined behavior when the array is * empty. */ - T &last() const + constexpr T &last() const { BLI_assert(size_ > 0); return data_[size_ - 1]; @@ -612,7 +615,7 @@ template<typename T> class MutableSpan { * Does a linear search to count how often the value is in the array. * Returns the number of occurrences. */ - int64_t count(const T &value) const + constexpr int64_t count(const T &value) const { int64_t counter = 0; for (const T &element : *this) { @@ -628,7 +631,7 @@ template<typename T> class MutableSpan { * destination contains uninitialized data and T is not trivially copy constructible. * The size of both spans is expected to be the same. */ - void copy_from(Span<T> values) + constexpr void copy_from(Span<T> values) { BLI_assert(size_ == values.size()); initialized_copy_n(values.data(), size_, data_); @@ -637,7 +640,7 @@ template<typename T> class MutableSpan { /** * Returns a new span to the same underlying memory buffer. No conversions are done. */ - template<typename NewT> MutableSpan<NewT> cast() const + template<typename NewT> constexpr MutableSpan<NewT> cast() const { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); @@ -648,7 +651,7 @@ template<typename T> class MutableSpan { /** * Utilities to check that arrays have the same size in debug builds. */ -template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2 &v2) +template<typename T1, typename T2> constexpr void assert_same_size(const T1 &v1, const T2 &v2) { UNUSED_VARS_NDEBUG(v1, v2); #ifdef DEBUG @@ -659,7 +662,7 @@ template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2 } template<typename T1, typename T2, typename T3> -void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3) +constexpr void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3) { UNUSED_VARS_NDEBUG(v1, v2, v3); #ifdef DEBUG diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 8597e54d03b..a2562c6100a 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -64,7 +64,7 @@ class StringRefBase { const char *data_; int64_t size_; - StringRefBase(const char *data, const int64_t size) : data_(data), size_(size) + constexpr StringRefBase(const char *data, const int64_t size) : data_(data), size_(size) { } @@ -75,12 +75,12 @@ class StringRefBase { /** * Return the (byte-)length of the referenced string, without any null-terminator. */ - int64_t size() const + constexpr int64_t size() const { return size_; } - bool is_empty() const + constexpr bool is_empty() const { return size_ == 0; } @@ -88,12 +88,12 @@ class StringRefBase { /** * Return a pointer to the start of the string. */ - const char *data() const + constexpr const char *data() const { return data_; } - operator Span<char>() const + constexpr operator Span<char>() const { return Span<char>(data_, size_); } @@ -107,22 +107,22 @@ class StringRefBase { return std::string(data_, static_cast<size_t>(size_)); } - operator std::string_view() const + constexpr operator std::string_view() const { return std::string_view(data_, static_cast<size_t>(size_)); } - const char *begin() const + constexpr const char *begin() const { return data_; } - const char *end() const + constexpr const char *end() const { return data_ + size_; } - IndexRange index_range() const + constexpr IndexRange index_range() const { return IndexRange(size_); } @@ -165,19 +165,19 @@ class StringRefBase { /** * Returns true when the string begins with the given prefix. Otherwise false. */ - bool startswith(StringRef prefix) const; + constexpr bool startswith(StringRef prefix) const; /** * Returns true when the string ends with the given suffix. Otherwise false. */ - bool endswith(StringRef suffix) const; + constexpr bool endswith(StringRef suffix) const; - StringRef substr(int64_t start, const int64_t size) const; + constexpr StringRef substr(int64_t start, const int64_t size) const; /** * Get the first char in the string. This invokes undefined behavior when the string is empty. */ - const char &front() const + constexpr const char &front() const { BLI_assert(size_ >= 1); return data_[0]; @@ -186,7 +186,7 @@ class StringRefBase { /** * Get the last char in the string. This invokes undefined behavior when the string is empty. */ - const char &back() const + constexpr const char &back() const { BLI_assert(size_ >= 1); return data_[size_ - 1]; @@ -196,18 +196,18 @@ class StringRefBase { * The behavior of those functions matches the standard library implementation of * std::string_view. */ - int64_t find(char c, int64_t pos = 0) const; - int64_t find(StringRef str, int64_t pos = 0) const; - int64_t rfind(char c, int64_t pos = INT64_MAX) const; - int64_t rfind(StringRef str, int64_t pos = INT64_MAX) const; - int64_t find_first_of(StringRef chars, int64_t pos = 0) const; - int64_t find_first_of(char c, int64_t pos = 0) const; - int64_t find_last_of(StringRef chars, int64_t pos = INT64_MAX) const; - int64_t find_last_of(char c, int64_t pos = INT64_MAX) const; - int64_t find_first_not_of(StringRef chars, int64_t pos = 0) const; - int64_t find_first_not_of(char c, int64_t pos = 0) const; - int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const; - int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const; + constexpr int64_t find(char c, int64_t pos = 0) const; + constexpr int64_t find(StringRef str, int64_t pos = 0) const; + constexpr int64_t rfind(char c, int64_t pos = INT64_MAX) const; + constexpr int64_t rfind(StringRef str, int64_t pos = INT64_MAX) const; + constexpr int64_t find_first_of(StringRef chars, int64_t pos = 0) const; + constexpr int64_t find_first_of(char c, int64_t pos = 0) const; + constexpr int64_t find_last_of(StringRef chars, int64_t pos = INT64_MAX) const; + constexpr int64_t find_last_of(char c, int64_t pos = INT64_MAX) const; + constexpr int64_t find_first_not_of(StringRef chars, int64_t pos = 0) const; + constexpr int64_t find_first_not_of(char c, int64_t pos = 0) const; + constexpr int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const; + constexpr int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const; }; /** @@ -216,7 +216,7 @@ class StringRefBase { class StringRefNull : public StringRefBase { public: - StringRefNull() : StringRefBase("", 0) + constexpr StringRefNull() : StringRefBase("", 0) { } @@ -226,7 +226,7 @@ class StringRefNull : public StringRefBase { */ StringRefNull(const char *str) : StringRefBase(str, static_cast<int64_t>(strlen(str))) { - BLI_assert(str != NULL); + BLI_assert(str != nullptr); BLI_assert(data_[size_] == '\0'); } @@ -234,7 +234,7 @@ class StringRefNull : public StringRefBase { * Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior * when the given size is not the correct size of the string. */ - StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size) + constexpr StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size) { BLI_assert(static_cast<int64_t>(strlen(str)) == size); } @@ -250,7 +250,7 @@ class StringRefNull : public StringRefBase { /** * Get the char at the given index. */ - char operator[](const int64_t index) const + constexpr char operator[](const int64_t index) const { BLI_assert(index >= 0); /* Use '<=' instead of just '<', so that the null character can be accessed as well. */ @@ -263,7 +263,7 @@ class StringRefNull : public StringRefBase { * * This is like ->data(), but can only be called on a StringRefNull. */ - const char *c_str() const + constexpr const char *c_str() const { return data_; } @@ -274,25 +274,26 @@ class StringRefNull : public StringRefBase { */ class StringRef : public StringRefBase { public: - StringRef() : StringRefBase(nullptr, 0) + constexpr StringRef() : StringRefBase(nullptr, 0) { } /** * StringRefNull can be converted into StringRef, but not the other way around. */ - StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) + constexpr StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) { } /** * Create a StringRef from a null-terminated c-string. */ - StringRef(const char *str) : StringRefBase(str, str ? static_cast<int64_t>(strlen(str)) : 0) + constexpr StringRef(const char *str) + : StringRefBase(str, str ? static_cast<int64_t>(std::char_traits<char>::length(str)) : 0) { } - StringRef(const char *str, const int64_t length) : StringRefBase(str, length) + constexpr StringRef(const char *str, const int64_t length) : StringRefBase(str, length) { } @@ -300,7 +301,7 @@ class StringRef : public StringRefBase { * Create a StringRef from a start and end pointer. This invokes undefined behavior when the * second point points to a smaller address than the first one. */ - StringRef(const char *begin, const char *one_after_end) + constexpr StringRef(const char *begin, const char *one_after_end) : StringRefBase(begin, static_cast<int64_t>(one_after_end - begin)) { BLI_assert(begin <= one_after_end); @@ -314,7 +315,8 @@ class StringRef : public StringRefBase { { } - StringRef(std::string_view view) : StringRefBase(view.data(), static_cast<int64_t>(view.size())) + constexpr StringRef(std::string_view view) + : StringRefBase(view.data(), static_cast<int64_t>(view.size())) { } @@ -323,7 +325,7 @@ class StringRef : public StringRefBase { * * This is similar to std::string_view::remove_prefix. */ - StringRef drop_prefix(const int64_t n) const + constexpr StringRef drop_prefix(const int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= size_); @@ -334,7 +336,7 @@ class StringRef : public StringRefBase { * Return a new StringRef with the given prefix being skipped. This invokes undefined behavior if * the string does not begin with the given prefix. */ - StringRef drop_prefix(StringRef prefix) const + constexpr StringRef drop_prefix(StringRef prefix) const { BLI_assert(this->startswith(prefix)); return this->drop_prefix(prefix.size()); @@ -345,7 +347,7 @@ class StringRef : public StringRefBase { * * This is similar to std::string_view::remove_suffix. */ - StringRef drop_suffix(const int64_t n) const + constexpr StringRef drop_suffix(const int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= size_); @@ -355,7 +357,7 @@ class StringRef : public StringRefBase { /** * Get the char at the given index. */ - char operator[](int64_t index) const + constexpr char operator[](int64_t index) const { BLI_assert(index >= 0); BLI_assert(index < size_); @@ -391,7 +393,7 @@ inline std::string operator+(StringRef a, StringRef b) * not a problem when std::string_view is only used at api boundaries. To compare a StringRef and a * std::string_view, one should convert the std::string_view to StringRef (which is very cheap). * Ideally, we only use StringRef in our code to avoid this problem altogether. */ -inline bool operator==(StringRef a, StringRef b) +constexpr inline bool operator==(StringRef a, StringRef b) { if (a.size() != b.size()) { return false; @@ -399,27 +401,27 @@ inline bool operator==(StringRef a, StringRef b) return STREQLEN(a.data(), b.data(), (size_t)a.size()); } -inline bool operator!=(StringRef a, StringRef b) +constexpr inline bool operator!=(StringRef a, StringRef b) { return !(a == b); } -inline bool operator<(StringRef a, StringRef b) +constexpr inline bool operator<(StringRef a, StringRef b) { return std::string_view(a) < std::string_view(b); } -inline bool operator>(StringRef a, StringRef b) +constexpr inline bool operator>(StringRef a, StringRef b) { return std::string_view(a) > std::string_view(b); } -inline bool operator<=(StringRef a, StringRef b) +constexpr inline bool operator<=(StringRef a, StringRef b) { return std::string_view(a) <= std::string_view(b); } -inline bool operator>=(StringRef a, StringRef b) +constexpr inline bool operator>=(StringRef a, StringRef b) { return std::string_view(a) >= std::string_view(b); } @@ -427,7 +429,7 @@ inline bool operator>=(StringRef a, StringRef b) /** * Return true when the string starts with the given prefix. */ -inline bool StringRefBase::startswith(StringRef prefix) const +constexpr inline bool StringRefBase::startswith(StringRef prefix) const { if (size_ < prefix.size_) { return false; @@ -443,7 +445,7 @@ inline bool StringRefBase::startswith(StringRef prefix) const /** * Return true when the string ends with the given suffix. */ -inline bool StringRefBase::endswith(StringRef suffix) const +constexpr inline bool StringRefBase::endswith(StringRef suffix) const { if (size_ < suffix.size_) { return false; @@ -460,8 +462,8 @@ inline bool StringRefBase::endswith(StringRef suffix) const /** * Return a new #StringRef containing only a sub-string of the original string. */ -inline StringRef StringRefBase::substr(const int64_t start, - const int64_t max_size = INT64_MAX) const +constexpr inline StringRef StringRefBase::substr(const int64_t start, + const int64_t max_size = INT64_MAX) const { BLI_assert(max_size >= 0); BLI_assert(start >= 0); @@ -469,7 +471,7 @@ inline StringRef StringRefBase::substr(const int64_t start, return StringRef(data_ + start, substr_size); } -inline int64_t index_or_npos_to_int64(size_t index) +constexpr inline int64_t index_or_npos_to_int64(size_t index) { /* The compiler will probably optimize this check away. */ if (index == std::string_view::npos) { @@ -478,62 +480,62 @@ inline int64_t index_or_npos_to_int64(size_t index) return static_cast<int64_t>(index); } -inline int64_t StringRefBase::find(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find(char c, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64(std::string_view(*this).find(c, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find(StringRef str, int64_t pos) const +constexpr inline int64_t StringRefBase::find(StringRef str, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_first_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const { return this->find_first_of(StringRef(&c, 1), pos); } -inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_last_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const { return this->find_last_of(StringRef(&c, 1), pos); } -inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_first_not_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const { return this->find_first_not_of(StringRef(&c, 1), pos); } -inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_last_not_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const { return this->find_last_not_of(StringRef(&c, 1), pos); } diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index d19b5393aa7..eefde1afefb 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -35,7 +35,6 @@ extern "C" { #define BLENDER_MAX_THREADS 1024 struct ListBase; -struct TaskScheduler; /* Threading API */ diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 053dcb6faea..fe6d54ae9e5 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -315,13 +315,13 @@ class Vector { return MutableSpan<T>(begin_, this->size()); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator Span<U>() const { return Span<U>(begin_, this->size()); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator MutableSpan<U>() { return MutableSpan<U>(begin_, this->size()); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 46a3ad87dfe..aa4c4efe7d4 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -203,6 +203,7 @@ set(SRC BLI_heap_simple.h BLI_index_mask.hh BLI_index_range.hh + BLI_inplace_priority_queue.hh BLI_iterator.h BLI_jitter_2d.h BLI_kdopbvh.h @@ -390,6 +391,7 @@ if(WITH_GTESTS) tests/BLI_heap_test.cc tests/BLI_index_mask_test.cc tests/BLI_index_range_test.cc + tests/BLI_inplace_priority_queue_test.cc tests/BLI_kdopbvh_test.cc tests/BLI_linear_allocator_test.cc tests/BLI_linklist_lockfree_test.cc diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index f126c5a977b..0f90ad3a490 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -1076,6 +1076,25 @@ float BLI_bvhtree_get_epsilon(const BVHTree *tree) return tree->epsilon; } +/** + * This function returns the bounding box of the BVH tree. + */ +void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3]) +{ + BVHNode *root = tree->nodes[tree->totleaf]; + if (root != NULL) { + const float bb_min[3] = {root->bv[0], root->bv[2], root->bv[4]}; + const float bb_max[3] = {root->bv[1], root->bv[3], root->bv[5]}; + copy_v3_v3(r_bb_min, bb_min); + copy_v3_v3(r_bb_max, bb_max); + } + else { + BLI_assert(false); + zero_v3(r_bb_min); + zero_v3(r_bb_max); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index d472ded0f18..ddaa067f50e 100644 --- a/source/blender/blenlib/tests/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -140,4 +140,11 @@ TEST(index_range, AsSpan) EXPECT_EQ(span[3], 7); } +TEST(index_range, constexpr_) +{ + constexpr IndexRange range = IndexRange(1, 1); + std::array<int, range[0]> compiles = {1}; + BLI_STATIC_ASSERT(range.size() == 1, ""); + EXPECT_EQ(compiles[0], 1); +} } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc b/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc new file mode 100644 index 00000000000..3adf12f36a7 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc @@ -0,0 +1,113 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_inplace_priority_queue.hh" +#include "BLI_rand.hh" + +namespace blender::tests { + +TEST(inplace_priority_queue, BuildSmall) +{ + Array<int> values = {1, 5, 2, 8, 5, 6, 5, 4, 3, 6, 7, 3}; + InplacePriorityQueue<int> priority_queue{values}; + + EXPECT_EQ(priority_queue.peek(), 8); + EXPECT_EQ(priority_queue.pop(), 8); + EXPECT_EQ(priority_queue.peek(), 7); + EXPECT_EQ(priority_queue.pop(), 7); + EXPECT_EQ(priority_queue.pop(), 6); + EXPECT_EQ(priority_queue.pop(), 6); + EXPECT_EQ(priority_queue.pop(), 5); +} + +TEST(inplace_priority_queue, DecreasePriority) +{ + Array<int> values = {5, 2, 7, 4}; + InplacePriorityQueue<int> priority_queue(values); + + EXPECT_EQ(priority_queue.peek(), 7); + values[2] = 0; + EXPECT_EQ(priority_queue.peek(), 0); + priority_queue.priority_decreased(2); + EXPECT_EQ(priority_queue.peek(), 5); +} + +TEST(inplace_priority_queue, IncreasePriority) +{ + Array<int> values = {5, 2, 7, 4}; + InplacePriorityQueue<int> priority_queue(values); + + EXPECT_EQ(priority_queue.peek(), 7); + values[1] = 10; + EXPECT_EQ(priority_queue.peek(), 7); + priority_queue.priority_increased(1); + EXPECT_EQ(priority_queue.peek(), 10); +} + +TEST(inplace_priority_queue, PopAll) +{ + RandomNumberGenerator rng; + Vector<int> values; + const int amount = 1000; + for (int i = 0; i < amount; i++) { + values.append(rng.get_int32() % amount); + } + + InplacePriorityQueue<int> priority_queue(values); + + int last_value = amount; + while (!priority_queue.is_empty()) { + const int value = priority_queue.pop(); + EXPECT_LE(value, last_value); + last_value = value; + } +} + +TEST(inplace_priority_queue, ManyPriorityChanges) +{ + RandomNumberGenerator rng; + Vector<int> values; + const int amount = 1000; + for (int i = 0; i < amount; i++) { + values.append(rng.get_int32() % amount); + } + + InplacePriorityQueue<int> priority_queue(values); + + for (int i = 0; i < amount; i++) { + const int index = rng.get_int32() % amount; + const int new_priority = rng.get_int32() % amount; + values[index] = new_priority; + priority_queue.priority_changed(index); + } + + int last_value = amount; + while (!priority_queue.is_empty()) { + const int value = priority_queue.pop(); + EXPECT_LE(value, last_value); + last_value = value; + } +} + +TEST(inplace_priority_queue, IndicesAccess) +{ + Array<int> values = {4, 6, 2, 4, 8, 1, 10, 2, 5}; + InplacePriorityQueue<int> priority_queue(values); + + EXPECT_EQ(priority_queue.active_indices().size(), 9); + EXPECT_EQ(priority_queue.inactive_indices().size(), 0); + EXPECT_EQ(priority_queue.all_indices().size(), 9); + EXPECT_EQ(priority_queue.pop(), 10); + EXPECT_EQ(priority_queue.active_indices().size(), 8); + EXPECT_EQ(priority_queue.inactive_indices().size(), 1); + EXPECT_EQ(values[priority_queue.inactive_indices()[0]], 10); + EXPECT_EQ(priority_queue.all_indices().size(), 9); + EXPECT_EQ(priority_queue.pop(), 8); + EXPECT_EQ(priority_queue.inactive_indices().size(), 2); + EXPECT_EQ(values[priority_queue.inactive_indices()[0]], 8); + EXPECT_EQ(values[priority_queue.inactive_indices()[1]], 10); + EXPECT_EQ(priority_queue.all_indices().size(), 9); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index fcef2f8688a..23415e69b04 100644 --- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -158,4 +158,15 @@ static_assert(is_convertible_pointer_v<int **, int **const>); static_assert(is_convertible_pointer_v<int **, int *const *>); static_assert(is_convertible_pointer_v<int **, int const *const *>); +static_assert(is_span_convertible_pointer_v<int *, int *>); +static_assert(is_span_convertible_pointer_v<int *, const int *>); +static_assert(!is_span_convertible_pointer_v<const int *, int *>); +static_assert(is_span_convertible_pointer_v<const int *, const int *>); +static_assert(is_span_convertible_pointer_v<const int *, const void *>); +static_assert(!is_span_convertible_pointer_v<const int *, void *>); +static_assert(is_span_convertible_pointer_v<int *, void *>); +static_assert(is_span_convertible_pointer_v<int *, const void *>); +static_assert(!is_span_convertible_pointer_v<TestBaseClass *, TestChildClass *>); +static_assert(!is_span_convertible_pointer_v<TestChildClass *, TestBaseClass *>); + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 9a8d9df7873..d1c9f312b97 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -337,4 +337,19 @@ TEST(span, MutableReverseIterator) EXPECT_EQ_ARRAY(src.data(), Span({14, 15, 16, 17}).data(), 4); } +TEST(span, constexpr_) +{ + static constexpr std::array<int, 3> src = {3, 2, 1}; + constexpr Span<int> span(src); + BLI_STATIC_ASSERT(span[2] == 1, ""); + BLI_STATIC_ASSERT(span.size() == 3, ""); + BLI_STATIC_ASSERT(span.slice(1, 2).size() == 2, ""); + BLI_STATIC_ASSERT(span.has_duplicates__linear_search() == false, ""); + + std::integral_constant<bool, span.first_index(1) == 2> ic; + BLI_STATIC_ASSERT(ic.value, ""); + + EXPECT_EQ(span.slice(1, 2).size(), 2); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc index 2d488feff71..401a7bc1118 100644 --- a/source/blender/blenlib/tests/BLI_string_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc @@ -298,4 +298,12 @@ TEST(string_ref, ToStringView) EXPECT_EQ(view, "hello"); } +TEST(string_ref, constexpr_) +{ + constexpr StringRef sref("World"); + BLI_STATIC_ASSERT(sref[2] == 'r', ""); + BLI_STATIC_ASSERT(sref.size() == 5, ""); + std::array<int, static_cast<std::size_t>(sref.find_first_of('o'))> compiles = {1}; + EXPECT_EQ(compiles[0], 1); +} } // namespace blender::tests diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index c4480e2c544..ea0532d884b 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -160,6 +160,7 @@ void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_p void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr); void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr); void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr); +void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr); void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr); void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr); void BLO_write_string(BlendWriter *writer, const char *data_ptr); diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 3e7cdb30e1f..c724cc32051 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -34,16 +34,13 @@ #include "zlib.h" struct BLOCacheStorage; -struct GSet; struct IDNameLib_Map; struct Key; struct MemFile; struct Object; struct OldNewMap; -struct PartEff; struct ReportList; struct UserDef; -struct View3D; typedef struct IDNameLib_Map IDNameLib_Map; diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index d00c5225299..4df681002a0 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -74,6 +74,29 @@ /* Make preferences read-only, use versioning_userdef.c. */ #define U (*((const UserDef *)&U)) +static eSpaceSeq_Proxy_RenderSize get_sequencer_render_size(Main *bmain) +{ + eSpaceSeq_Proxy_RenderSize render_size = 100; + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_SEQ: { + SpaceSeq *sseq = (SpaceSeq *)sl; + if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { + render_size = sseq->render_size; + break; + } + } + } + } + } + } + + return render_size; +} + /* image_size is width or height depending what RNA property is converted - X or Y. */ static void seq_convert_transform_animation(const Scene *scene, const char *path, @@ -212,6 +235,84 @@ static void seq_convert_transform_crop_lb(const Scene *scene, } } +static void seq_convert_transform_animation_2(const Scene *scene, + const char *path, + const float scale_to_fit_factor) +{ + if (scene->adt == NULL || scene->adt->action == NULL) { + return; + } + + FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0); + if (fcu != NULL && !BKE_fcurve_is_empty(fcu)) { + BezTriple *bezt = fcu->bezt; + for (int i = 0; i < fcu->totvert; i++, bezt++) { + /* Same math as with old_image_center_*, but simplified. */ + bezt->vec[1][1] *= scale_to_fit_factor; + } + } +} + +static void seq_convert_transform_crop_2(const Scene *scene, + Sequence *seq, + const eSpaceSeq_Proxy_RenderSize render_size) +{ + const StripElem *s_elem = SEQ_render_give_stripelem(seq, seq->start); + if (s_elem == NULL) { + return; + } + + StripCrop *c = seq->strip->crop; + StripTransform *t = seq->strip->transform; + int image_size_x = s_elem->orig_width; + int image_size_y = s_elem->orig_height; + + if (SEQ_can_use_proxy(seq, SEQ_rendersize_to_proxysize(render_size))) { + image_size_x /= SEQ_rendersize_to_scale_factor(render_size); + image_size_y /= SEQ_rendersize_to_scale_factor(render_size); + } + + /* Calculate scale factor, so image fits in preview area with original aspect ratio. */ + const float scale_to_fit_factor = MIN2((float)scene->r.xsch / (float)image_size_x, + (float)scene->r.ysch / (float)image_size_y); + t->scale_x *= scale_to_fit_factor; + t->scale_y *= scale_to_fit_factor; + c->top /= scale_to_fit_factor; + c->bottom /= scale_to_fit_factor; + c->left /= scale_to_fit_factor; + c->right /= scale_to_fit_factor; + + char name_esc[(sizeof(seq->name) - 2) * 2], *path; + BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_x", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_y", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); +} + +static void seq_convert_transform_crop_lb_2(const Scene *scene, + const ListBase *lb, + const eSpaceSeq_Proxy_RenderSize render_size) +{ + + LISTBASE_FOREACH (Sequence *, seq, lb) { + if (seq->type != SEQ_TYPE_SOUND_RAM) { + seq_convert_transform_crop_2(scene, seq, render_size); + } + if (seq->type == SEQ_TYPE_META) { + seq_convert_transform_crop_lb_2(scene, &seq->seqbase, render_size); + } + } +} + void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) { if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) { @@ -441,25 +542,35 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) if (!MAIN_VERSION_ATLEAST(bmain, 292, 2)) { - eSpaceSeq_Proxy_RenderSize render_size = 100; + eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain); - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - switch (sl->spacetype) { - case SPACE_SEQ: { - SpaceSeq *sseq = (SpaceSeq *)sl; - render_size = sseq->render_size; - break; - } - } - } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + seq_convert_transform_crop_lb(scene, &scene->ed->seqbase, render_size); } } + } + if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) { + /* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in + * Armature obdata. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type == OB_ARMATURE) { + BKE_pose_rebuild(bmain, ob, ob->data, true); + } + } + + /* Wet Paint Radius Factor */ + for (Brush *br = bmain->brushes.first; br; br = br->id.next) { + if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) { + br->wet_paint_radius_factor = 1.0f; + } + } + + eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain); LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->ed != NULL) { - seq_convert_transform_crop_lb(scene, &scene->ed->seqbase, render_size); + seq_convert_transform_crop_lb_2(scene, &scene->ed->seqbase, render_size); } } } @@ -476,21 +587,6 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ - - /* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in - * Armature obdata. */ - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->type == OB_ARMATURE) { - BKE_pose_rebuild(bmain, ob, ob->data, true); - } - } - } - - /* Wet Paint Radius Factor */ - for (Brush *br = bmain->brushes.first; br; br = br->id.next) { - if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) { - br->wet_paint_radius_factor = 1.0f; - } } } @@ -1234,18 +1330,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 292, 7)) { /* Make all IDProperties used as interface of geometry node trees overridable. */ LISTBASE_FOREACH (Object *, ob, &bmain->objects) { LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { @@ -1301,5 +1386,47 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Overlay elements in the sequencer. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->flag |= (SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_SOURCE | + SEQ_SHOW_STRIP_DURATION); + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (STREQ(node->idname, "GeometryNodeRandomAttribute")) { + STRNCPY(node->idname, "GeometryNodeAttributeRandomize"); + } + } + } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index f89a5be27de..198f65b9794 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -182,7 +182,8 @@ static void blo_update_defaults_screen(bScreen *screen, } else if (area->spacetype == SPACE_SEQ) { SpaceSeq *seq = area->spacedata.first; - seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT; + seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | + SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION; } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ @@ -738,6 +739,14 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_ERASER; } + brush_name = "Multires Displacement Smear"; + brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + if (!brush) { + brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); + id_us_min(&brush->id); + brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR; + } + /* Use the same tool icon color in the brush cursor */ for (brush = bmain->brushes.first; brush; brush = brush->id.next) { if (brush->ob_mode & OB_MODE_SCULPT) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index e7d55538f7e..0a4f2fde93f 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1374,6 +1374,11 @@ void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr) BLO_write_raw(writer, sizeof(float) * (size_t)num, data_ptr); } +void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr) +{ + BLO_write_raw(writer, sizeof(double) * (size_t)num, data_ptr); +} + void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr) { BLO_write_raw(writer, sizeof(void *) * (size_t)num, data_ptr); diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index f894bdabba4..4e618d8625d 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -39,7 +39,6 @@ struct Main; struct Object; struct Scene; struct Simulation; -struct ViewLayer; struct bNodeTree; #include "BLI_sys_types.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index cbfb51c59a6..5587379089c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -45,7 +45,6 @@ #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" -struct Base; struct CacheFile; struct Camera; struct Collection; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h index 1f58c54dbf4..ec661360fdf 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h @@ -25,8 +25,6 @@ #pragma once -struct Main; - namespace blender { namespace deg { diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.h b/source/blender/draw/engines/eevee/eevee_lightcache.h index 17392c0de0b..fde0c80ab37 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.h +++ b/source/blender/draw/engines/eevee/eevee_lightcache.h @@ -24,14 +24,14 @@ #include "BLI_sys_types.h" /* for bool */ +struct BlendDataReader; +struct BlendWriter; struct EEVEE_Data; struct EEVEE_ViewLayerData; struct LightCache; struct Scene; struct SceneEEVEE; struct ViewLayer; -struct BlendWriter; -struct BlendDataReader; /* Light Bake */ struct wmJob *EEVEE_lightbake_job_create(struct wmWindowManager *wm, diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 04128dc157e..d0bd56b42dd 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -39,10 +39,7 @@ extern DrawEngineType draw_engine_gpencil_type; struct GPENCIL_Data; struct GPENCIL_StorageList; struct GPUBatch; -struct GPUVertBuf; -struct GPUVertFormat; struct GpencilBatchCache; -struct MaterialGPencilStyle; struct Object; struct RenderEngine; struct RenderLayer; diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.h index d5821cc5d70..76a94e68da1 100644 --- a/source/blender/draw/engines/image/image_private.h +++ b/source/blender/draw/engines/image/image_private.h @@ -25,11 +25,9 @@ extern "C" { #endif /* Forward declarations */ -struct GPUBatch; struct GPUTexture; struct ImBuf; struct Image; -struct rcti; /* *********** LISTS *********** */ diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index fbe71900915..4df2ba1e913 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -64,7 +64,6 @@ #include "DEG_depsgraph.h" struct GPUBatch; -struct GPUFrameBuffer; struct GPUMaterial; struct GPUShader; struct GPUTexture; diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 23dd47d4ab5..a059ac32311 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -22,10 +22,8 @@ #pragma once -struct DRWPass; struct DRWShadingGroup; struct FluidModifierData; -struct GPUMaterial; struct ModifierData; struct Object; struct ParticleSystem; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 988975bd399..ffc565d0514 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2073,7 +2073,6 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, * for the image editor this is when showing UV's.*/ const bool do_populate_loop = (DST.draw_ctx.space_data->spacetype == SPACE_IMAGE); const bool do_annotations = drw_draw_show_annotation(); - const bool do_region_callbacks = (DST.draw_ctx.space_data->spacetype != SPACE_IMAGE); const bool do_draw_gizmos = (DST.draw_ctx.space_data->spacetype != SPACE_IMAGE); /* Get list of enabled engines */ @@ -2125,7 +2124,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, /* Start Drawing */ DRW_state_reset(); - if (do_region_callbacks && DST.draw_ctx.evil_C) { + if (DST.draw_ctx.evil_C) { ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_PRE_VIEW); } @@ -2147,10 +2146,8 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, if (do_annotations) { ED_annotation_draw_view2d(DST.draw_ctx.evil_C, true); } - if (do_region_callbacks) { - GPU_depth_test(GPU_DEPTH_NONE); - ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); - } + GPU_depth_test(GPU_DEPTH_NONE); + ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); GPU_matrix_pop_projection(); /* Callback can be nasty and do whatever they want with the state. * Don't trust them! */ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 3617f20763e..9bca294cf30 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -283,8 +283,8 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); tgpil->gpl = gpl; - tgpil->prevFrame = gpl->actframe; - tgpil->nextFrame = gpl->actframe->next; + tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe); + tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next); BLI_addtail(&tgpi->ilayers, tgpil); @@ -326,24 +326,25 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) valid = false; } - /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); - if (valid) { /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { - new_stroke->points = MEM_recallocN(new_stroke->points, - sizeof(*new_stroke->points) * gps_to->totpoints); - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, - sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - new_stroke->totpoints = gps_to->totpoints; + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true); } - /* update points position */ + if (gps_to->totpoints > gps_from->totpoints) { + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true); + } + + /* Create new stroke. */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); + + /* Update points position. */ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); } else { + /* Create new stroke. */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); + /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ new_stroke->totpoints = 0; new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); @@ -443,12 +444,16 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) /* finally, free memory used by temp data */ LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) { + BKE_gpencil_free_strokes(tgpil->prevFrame); + BKE_gpencil_free_strokes(tgpil->nextFrame); BKE_gpencil_free_strokes(tgpil->interFrame); - MEM_freeN(tgpil->interFrame); + MEM_SAFE_FREE(tgpil->prevFrame); + MEM_SAFE_FREE(tgpil->nextFrame); + MEM_SAFE_FREE(tgpil->interFrame); } BLI_freelistN(&tgpi->ilayers); - MEM_freeN(tgpi); + MEM_SAFE_FREE(tgpi); } DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); @@ -992,8 +997,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* store extremes */ - prevFrame = gpl->actframe; - nextFrame = gpl->actframe->next; + prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe); + nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next); /* Loop over intermediary frames and create the interpolation */ for (cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) { @@ -1049,28 +1054,17 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; } - /* create new stroke */ - bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { - /* free weights of removed points */ - if (new_stroke->dvert != NULL) { - BKE_defvert_array_free_elems(new_stroke->dvert + gps_to->totpoints, - gps_from->totpoints - gps_to->totpoints); - } - - new_stroke->points = MEM_recallocN(new_stroke->points, - sizeof(*new_stroke->points) * gps_to->totpoints); - - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, - sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - - new_stroke->totpoints = gps_to->totpoints; + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true); + } + if (gps_to->totpoints > gps_from->totpoints) { + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true); } + /* create new stroke */ + bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); + /* update points position */ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor); @@ -1081,6 +1075,11 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) BLI_addtail(&interFrame->strokes, new_stroke); } } + + BKE_gpencil_free_strokes(prevFrame); + BKE_gpencil_free_strokes(nextFrame); + MEM_SAFE_FREE(prevFrame); + MEM_SAFE_FREE(nextFrame); } /* notifiers */ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 3501acd4fdf..0c4576096fb 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -31,7 +31,6 @@ struct Base; struct Bone; struct Depsgraph; struct EditBone; -struct IDProperty; struct ListBase; struct Main; struct Mesh; diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 3288cf11cb0..7b240e0569f 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -28,8 +28,9 @@ extern "C" { #endif struct ARegion; -struct FileSelectParams; struct FileAssetSelectParams; +struct FileSelectParams; +struct FileDirEntry; struct Scene; struct ScrArea; struct SpaceFile; @@ -154,6 +155,7 @@ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win, int ED_path_extension_type(const char *path); int ED_file_extension_icon(const char *path); +int ED_file_icon(const struct FileDirEntry *file); void ED_file_read_bookmarks(void); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index be2f714dfe1..1b7caf27ecf 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -51,7 +51,6 @@ struct ScrArea; struct SnapObjectContext; struct ToolSettings; struct View3D; -struct ViewLayer; struct bContext; struct Material; diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index c1d3a17b9b6..b139b0765a3 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -34,12 +34,10 @@ struct ARegion; struct ImBuf; struct Image; struct ImageUser; -struct LinkNodePair; struct Main; struct ReportList; struct Scene; struct SpaceImage; -struct ViewLayer; struct bContext; struct wmOperator; struct wmWindowManager; diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 2e9b711c99a..f9358f62274 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -53,7 +53,6 @@ struct uiLayout; struct wmKeyConfig; struct wmOperator; struct wmOperatorType; -struct wmWindowManager; /* object_edit.c */ /* context.object */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index dc1c43c0337..20417634020 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -239,6 +239,7 @@ void ED_screen_restore_temp_type(struct bContext *C, ScrArea *area); ScrArea *ED_screen_full_newspace(struct bContext *C, ScrArea *area, int type); void ED_screen_full_prevspace(struct bContext *C, ScrArea *area); void ED_screen_full_restore(struct bContext *C, ScrArea *area); +ScrArea *ED_screen_state_maximized_create(struct bContext *C); struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *area, diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 0ea86e006e0..ca3e351a052 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -32,7 +32,6 @@ extern "C" { struct Object; struct bContext; struct wmKeyConfig; -struct wmMsgBus; struct wmOperatorType; void ED_keymap_transform(struct wmKeyConfig *keyconf); @@ -108,7 +107,6 @@ bool calculateTransformCenter(struct bContext *C, struct Object; struct Scene; -struct wmGizmoGroup; struct wmGizmoGroupType; /* UNUSED */ diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index ebaa32941f2..b7174964ef6 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -31,7 +31,6 @@ struct BMVert; struct ARegion; struct Depsgraph; struct ListBase; -struct Main; struct Object; struct Scene; struct View3D; diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index d74a80045f1..ca6b4bdc618 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -54,6 +54,7 @@ void ED_spacedata_id_remap(struct ScrArea *area, void ED_OT_flush_edits(struct wmOperatorType *ot); void ED_OT_lib_id_load_custom_preview(struct wmOperatorType *ot); +void ED_OT_lib_id_generate_preview(struct wmOperatorType *ot); /* ************** XXX OLD CRUFT WARNING ************* */ diff --git a/source/blender/editors/include/ED_util_imbuf.h b/source/blender/editors/include/ED_util_imbuf.h index d142d3d6425..4bbaa68e849 100644 --- a/source/blender/editors/include/ED_util_imbuf.h +++ b/source/blender/editors/include/ED_util_imbuf.h @@ -31,7 +31,6 @@ extern "C" { #endif struct ARegion; -struct Main; struct bContext; struct wmEvent; struct wmOperator; diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index d08bc0b0b3d..4de97411059 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -33,7 +33,6 @@ struct BMEditMesh; struct BMFace; struct BMLoop; struct BMesh; -struct Depsgraph; struct Image; struct ImageUser; struct Main; diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 13687bf0450..a4856845a65 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -41,8 +41,6 @@ struct Camera; struct CustomData_MeshMasks; struct Depsgraph; struct EditBone; -struct GPUOffScreen; -struct GPUViewport; struct ID; struct MVert; struct Main; @@ -55,7 +53,6 @@ struct RenderEngineType; struct Scene; struct ScrArea; struct View3D; -struct View3DShading; struct ViewContext; struct ViewLayer; struct bContext; @@ -64,8 +61,6 @@ struct bScreen; struct rctf; struct rcti; struct wmGizmo; -struct wmOperator; -struct wmOperatorType; struct wmWindow; struct wmWindowManager; diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 83e94664c0b..7c128cbf1e6 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -50,7 +50,6 @@ struct PointerRNA; struct PropertyRNA; struct ReportList; struct ResultBLF; -struct ScrArea; struct bContext; struct bContextStore; struct bNode; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 1c09dc6f4df..899f4a6ddb1 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1416,7 +1416,7 @@ static void icon_set_image(const bContext *C, const bool delay = prv_img->rect[size] != NULL; icon_create_rect(prv_img, size); - if (use_job && BKE_previewimg_id_supports_jobs(id)) { + if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) { /* Job (background) version */ ED_preview_icon_job( C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay); @@ -2199,6 +2199,9 @@ int UI_icon_from_library(const ID *id) if (ID_IS_OVERRIDE_LIBRARY(id)) { return ICON_LIBRARY_DATA_OVERRIDE; } + if (ID_IS_ASSET(id)) { + return ICON_MAT_SPHERE_SKY; + } return ICON_NONE; } diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 8ec9f7af184..5caa7c71e83 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2527,7 +2527,7 @@ static bool convert_proxy_to_override_poll(bContext *C) return obact != NULL && obact->proxy != NULL; } -static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op)) +static int convert_proxy_to_override_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -2541,6 +2541,15 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op)) const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, view_layer, ob_proxy); + if (!success) { + BKE_reportf( + op->reports, + RPT_ERROR_INVALID_INPUT, + "Could not create a library override from proxy '%s' (might use already local data?)", + ob_proxy->id.name + 2); + return OPERATOR_CANCELLED; + } + /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ if (success && is_override_instancing_object) { diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 3d6a6abfe0d..23f1718cb2e 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -741,6 +741,9 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext * RNA_enum_items_add_value( &item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_SELECT); } + } + + if (BKE_modifiers_is_deformed_by_armature(ob)) { if (selection_mask & (1 << WT_VGROUP_BONE_DEFORM)) { RNA_enum_items_add_value( &item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_DEFORM); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 3dbf70aa4bc..a035ee3e342 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -150,31 +150,31 @@ static void image_buffer_rect_update(RenderJob *rj, } /* xmin here is first subrect x coord, xmax defines subrect width */ - xmin = renrect->xmin + rr->crop; - xmax = renrect->xmax - xmin + rr->crop; + xmin = renrect->xmin; + xmax = renrect->xmax - xmin; if (xmax < 2) { return; } - ymin = renrect->ymin + rr->crop; - ymax = renrect->ymax - ymin + rr->crop; + ymin = renrect->ymin; + ymax = renrect->ymax - ymin; if (ymax < 2) { return; } renrect->ymin = renrect->ymax; } else { - xmin = ymin = rr->crop; - xmax = rr->rectx - 2 * rr->crop; - ymax = rr->recty - 2 * rr->crop; + xmin = ymin = 0; + xmax = rr->rectx; + ymax = rr->recty; } /* xmin ymin is in tile coords. transform to ibuf */ - rxmin = rr->tilerect.xmin + xmin; + rxmin = rr->tilerect.xmin; if (rxmin >= ibuf->x) { return; } - rymin = rr->tilerect.ymin + ymin; + rymin = rr->tilerect.ymin; if (rymin >= ibuf->y) { return; } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 6be2fb8004b..be52874ed0b 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1133,12 +1133,11 @@ void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene) ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) { - wmWindow *win = CTX_wm_window(C); ScrArea *newsa = NULL; SpaceLink *newsl; if (!area || area->full == NULL) { - newsa = ED_screen_state_toggle(C, win, area, SCREENMAXIMIZED); + newsa = ED_screen_state_maximized_create(C); } if (!newsa) { @@ -1149,11 +1148,11 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) newsl = newsa->spacedata.first; /* Tag the active space before changing, so we can identify it when user wants to go back. */ - if ((newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { + if (newsl && (newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { newsl->link_flag |= SPACE_FLAG_TYPE_WAS_ACTIVE; } - ED_area_newspace(C, newsa, type, newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY); + ED_area_newspace(C, newsa, type, (newsl && newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY)); return newsa; } @@ -1217,13 +1216,108 @@ void ED_screen_full_restore(bContext *C, ScrArea *area) } /** - * this function toggles: if area is maximized/full then the parent will be restored + * \param toggle_area: If this is set, its space data will be swapped with the one of the new emtpy + * area, when toggling back it can be swapped back again. + * \return The newly created screen with the non-normal area. + */ +static bScreen *screen_state_to_nonnormal(bContext *C, + wmWindow *win, + ScrArea *toggle_area, + int state) +{ + Main *bmain = CTX_data_main(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); + + /* change from SCREENNORMAL to new state */ + WorkSpaceLayout *layout_new; + ScrArea *newa; + char newname[MAX_ID_NAME - 2]; + + BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL)); + + bScreen *oldscreen = WM_window_get_active_screen(win); + + oldscreen->state = state; + BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal"); + + layout_new = ED_workspace_layout_add(bmain, workspace, win, newname); + + bScreen *screen = BKE_workspace_layout_screen_get(layout_new); + screen->state = state; + screen->redraws_flag = oldscreen->redraws_flag; + screen->temp = oldscreen->temp; + screen->flag = oldscreen->flag; + + /* timer */ + screen->animtimer = oldscreen->animtimer; + oldscreen->animtimer = NULL; + + newa = (ScrArea *)screen->areabase.first; + + /* swap area */ + if (toggle_area) { + ED_area_data_swap(newa, toggle_area); + newa->flag = toggle_area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */ + } + + if (state == SCREENFULL) { + /* temporarily hide global areas */ + LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) { + glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN; + } + /* temporarily hide the side panels/header */ + LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) { + region->flagfullscreen = region->flag; + + if (ELEM(region->regiontype, + RGN_TYPE_UI, + RGN_TYPE_HEADER, + RGN_TYPE_TOOL_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_TOOLS, + RGN_TYPE_NAV_BAR, + RGN_TYPE_EXECUTE)) { + region->flag |= RGN_FLAG_HIDDEN; + } + } + } + + if (toggle_area) { + toggle_area->full = oldscreen; + } + newa->full = oldscreen; + + ED_screen_change(C, screen); + ED_area_tag_refresh(newa); + + return screen; +} + +/** + * Create a new temporary screen with a maximized, empty area. + * This can be closed with #ED_screen_state_toggle(). + * + * Use this to just create a new maximized screen/area, rather than maximizing an existing one. + * Otherwise, maximize with #ED_screen_state_toggle(). + */ +ScrArea *ED_screen_state_maximized_create(bContext *C) +{ + bScreen *screen = screen_state_to_nonnormal(C, CTX_wm_window(C), NULL, SCREENMAXIMIZED); + return screen->areabase.first; +} + +/** + * This function toggles: if area is maximized/full then the parent will be restored. + * + * Use #ED_screen_state_maximized_create() if you do not want the toggle behavior when changing to + * a maximized area. I.e. if you just want to open a new maximized screen/area, not maximize a + * specific area. In the former case, space data of the maximized and non-maximized area should be + * independent, in the latter it should be the same. * * \warning \a area may be freed. */ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const short state) { - Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); WorkSpace *workspace = WM_window_get_active_workspace(win); @@ -1257,7 +1351,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const screen->state = SCREENNORMAL; screen->flag = oldscreen->flag; - /* find old area to restore from */ + /* Find old area we may have swapped dummy space data to. It's swapped back here. */ ScrArea *fullsa = NULL; LISTBASE_FOREACH (ScrArea *, old, &screen->areabase) { /* area to restore from is always first */ @@ -1271,13 +1365,6 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const area->full = NULL; - if (fullsa == NULL) { - if (G.debug & G_DEBUG) { - printf("%s: something wrong in areafullscreen\n", __func__); - } - return NULL; - } - if (state == SCREENFULL) { /* unhide global areas */ LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) { @@ -1289,14 +1376,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const } } - ED_area_data_swap(fullsa, area); + if (fullsa) { + ED_area_data_swap(fullsa, area); + ED_area_tag_refresh(fullsa); + } /* animtimer back */ screen->animtimer = oldscreen->animtimer; oldscreen->animtimer = NULL; ED_screen_change(C, screen); - ED_area_tag_refresh(fullsa); BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old); @@ -1307,68 +1396,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const screen->skip_handling = true; } else { - /* change from SCREENNORMAL to new state */ - WorkSpaceLayout *layout_new; - ScrArea *newa; - char newname[MAX_ID_NAME - 2]; - - BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL)); - - bScreen *oldscreen = WM_window_get_active_screen(win); - - oldscreen->state = state; - BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal"); - - layout_new = ED_workspace_layout_add(bmain, workspace, win, newname); - - screen = BKE_workspace_layout_screen_get(layout_new); - screen->state = state; - screen->redraws_flag = oldscreen->redraws_flag; - screen->temp = oldscreen->temp; - screen->flag = oldscreen->flag; - - /* timer */ - screen->animtimer = oldscreen->animtimer; - oldscreen->animtimer = NULL; + ScrArea *toggle_area = area; /* use random area when we have no active one, e.g. when the * mouse is outside of the window and we open a file browser */ - if (!area || area->global) { - area = oldscreen->areabase.first; + if (!toggle_area || toggle_area->global) { + bScreen *oldscreen = WM_window_get_active_screen(win); + toggle_area = oldscreen->areabase.first; } - newa = (ScrArea *)screen->areabase.first; - - /* copy area */ - ED_area_data_swap(newa, area); - newa->flag = area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */ - - if (state == SCREENFULL) { - /* temporarily hide global areas */ - LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) { - glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN; - } - /* temporarily hide the side panels/header */ - LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) { - region->flagfullscreen = region->flag; - - if (ELEM(region->regiontype, - RGN_TYPE_UI, - RGN_TYPE_HEADER, - RGN_TYPE_TOOL_HEADER, - RGN_TYPE_FOOTER, - RGN_TYPE_TOOLS, - RGN_TYPE_NAV_BAR, - RGN_TYPE_EXECUTE)) { - region->flag |= RGN_FLAG_HIDDEN; - } - } - } - - area->full = oldscreen; - newa->full = oldscreen; - - ED_screen_change(C, screen); + screen = screen_state_to_nonnormal(C, win, toggle_area, state); } /* XXX bad code: setscreen() ends with first area active. fullscreen render assumes this too */ @@ -1412,9 +1449,6 @@ ScrArea *ED_screen_temp_space_open(bContext *C, area->flag |= AREA_FLAG_STACKED_FULLSCREEN; ((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; } - else if (ctx_area != NULL && ctx_area->spacetype == space_type) { - area = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_area, SCREENMAXIMIZED); - } else { area = ED_screen_full_newspace(C, ctx_area, (int)space_type); ((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index a0c5762c73c..51687d5de1d 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -5511,6 +5511,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(ED_OT_flush_edits); WM_operatortype_append(ED_OT_lib_id_load_custom_preview); + WM_operatortype_append(ED_OT_lib_id_generate_preview); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 9bf3d2610d8..fff8d27ef5b 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC ../../windowmanager ../../../../intern/atomic ../../../../intern/clog + ../../../../intern/eigen ../../../../intern/glew-mx ../../../../intern/guardedalloc ) diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 175d98ba9aa..3ca0d853d6a 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -43,7 +43,6 @@ struct wmEvent; struct wmKeyConfig; struct wmOperator; struct wmOperatorType; -struct wmWindowManager; enum ePaintMode; enum ePaintSymmetryFlags; diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 17690757fa5..92c78a674f0 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1552,6 +1552,11 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (ss->totvert == 0) { + /* No geometry to trim or to detect a valid position for the trimming shape. */ + return OPERATOR_CANCELLED; + } + SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); if (!sgcontext) { return OPERATOR_CANCELLED; @@ -1589,6 +1594,11 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (ss->totvert == 0) { + /* No geometry to trim or to detect a valid position for the trimming shape. */ + return OPERATOR_CANCELLED; + } + SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op); if (!sgcontext) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 94f05560f79..38d2bed7d97 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1227,6 +1227,7 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool) SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER, SCULPT_TOOL_POSE, + SCULPT_TOOL_DISPLACEMENT_SMEAR, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_CLOTH, SCULPT_TOOL_PAINT, @@ -2362,6 +2363,7 @@ static float brush_strength(const Sculpt *sd, final_pressure = pressure * pressure; return final_pressure * overlap * feather; case SCULPT_TOOL_SMEAR: + case SCULPT_TOOL_DISPLACEMENT_SMEAR: return alpha * pressure * overlap * feather; case SCULPT_TOOL_CLAY_STRIPS: /* Clay Strips needs less strength to compensate the curve. */ @@ -3103,6 +3105,147 @@ static void do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **node /** \} */ +/** \name Sculpt Multires Displacement Smear Brush + * \{ */ + +static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + float current_disp[3]; + float current_disp_norm[3]; + float interp_limit_surface_disp[3]; + + copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]); + + switch (brush->smear_deform_type) { + case BRUSH_SMEAR_DEFORM_DRAG: + sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + break; + case BRUSH_SMEAR_DEFORM_PINCH: + sub_v3_v3v3(current_disp, ss->cache->location, vd.co); + break; + case BRUSH_SMEAR_DEFORM_EXPAND: + sub_v3_v3v3(current_disp, vd.co, ss->cache->location); + break; + } + + normalize_v3_v3(current_disp_norm, current_disp); + mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); + + float weights_accum = 1.0f; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + float vertex_disp[3]; + float vertex_disp_norm[3]; + float neighbor_limit_co[3]; + SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co); + sub_v3_v3v3(vertex_disp, + ss->cache->limit_surface_co[ni.index], + ss->cache->limit_surface_co[vd.index]); + const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index]; + normalize_v3_v3(vertex_disp_norm, vertex_disp); + if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) { + const float disp_interp = clamp_f( + -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f); + madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp); + weights_accum += disp_interp; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum); + + float new_co[3]; + add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp); + interp_v3_v3v3(vd.co, vd.co, new_co, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_displacement_smear_store_prev_disp_task_cb_ex( + void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sub_v3_v3v3(ss->cache->prev_displacement[vd.index], + SCULPT_vertex_co_get(ss, vd.index), + ss->cache->limit_surface_co[vd.index]); + } + BKE_pbvh_vertex_iter_end; +} + +static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + + BKE_curvemapping_init(brush->curve); + + const int totvert = SCULPT_vertex_count_get(ss); + if (!ss->cache->prev_displacement) { + ss->cache->prev_displacement = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "prev displacement"); + ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co"); + for (int i = 0; i < totvert; i++) { + SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]); + sub_v3_v3v3(ss->cache->prev_displacement[i], + SCULPT_vertex_co_get(ss, i), + ss->cache->limit_surface_co[i]); + } + } + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range( + 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings); +} + +/** \} */ + static void do_draw_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -5742,32 +5885,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); } else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { - if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)), - .original = false, - .ignore_fully_ineffective = false, - .center = ss->cache->initial_location, - }; - BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); - } - if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)), - .original = false, - .ignore_fully_ineffective = false, - .center = ss->cache->location, - }; - BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); - } - else { - /* Gobal simulation, get all nodes. */ - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - } + nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); } else { const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : @@ -5805,6 +5923,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe } } + /* Initialize automasking cache. For anchored brushes with spherical falloff, we start off with + * zero radius, thus we have no pbvh nodes on the first brush step. */ + if (totnode || + ((brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) && (brush->flag & BRUSH_ANCHORED))) { + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + if (SCULPT_is_automasking_enabled(sd, ss, brush)) { + ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); + } + } + } + /* Only act if some verts are inside the brush area. */ if (totnode) { float location[3]; @@ -5828,12 +5957,6 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe update_brush_local_mat(sd, ob); } - if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - if (SCULPT_is_automasking_enabled(sd, ss, brush)) { - ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); - } - } - if (brush->sculpt_tool == SCULPT_TOOL_POSE && SCULPT_stroke_is_first_brush_step(ss->cache)) { SCULPT_pose_brush_init(sd, ob, ss, brush); } @@ -5952,6 +6075,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_DISPLACEMENT_ERASER: do_displacement_eraser_brush(sd, ob, nodes, totnode); break; + case SCULPT_TOOL_DISPLACEMENT_SMEAR: + do_displacement_smear_brush(sd, ob, nodes, totnode); + break; case SCULPT_TOOL_PAINT: SCULPT_do_paint_brush(sd, ob, nodes, totnode); break; @@ -6515,6 +6641,8 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Draw Face Sets"; case SCULPT_TOOL_DISPLACEMENT_ERASER: return "Multires Displacement Eraser"; + case SCULPT_TOOL_DISPLACEMENT_SMEAR: + return "Multires Displacement Smear"; case SCULPT_TOOL_PAINT: return "Paint Brush"; case SCULPT_TOOL_SMEAR: @@ -6535,6 +6663,8 @@ void SCULPT_cache_free(StrokeCache *cache) MEM_SAFE_FREE(cache->layer_displacement_factor); MEM_SAFE_FREE(cache->prev_colors); MEM_SAFE_FREE(cache->detail_directions); + MEM_SAFE_FREE(cache->prev_displacement); + MEM_SAFE_FREE(cache->limit_surface_co); if (cache->pose_ik_chain) { SCULPT_pose_ik_chain_free(cache->pose_ik_chain); @@ -6706,6 +6836,7 @@ static void sculpt_update_cache_invariants( SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY, + SCULPT_TOOL_DISPLACEMENT_SMEAR, SCULPT_TOOL_DISPLACEMENT_ERASER) && (sd->gravity_factor > 0.0f)); /* Get gravity vector in world space. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index f8165890cc4..0ac0d796ca4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -119,6 +119,43 @@ static void cloth_brush_simulation_location_get(SculptSession *ss, copy_v3_v3(r_location, ss->cache->location); } +PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss, + Brush *brush, + int *r_totnode) +{ + BLI_assert(ss->cache); + BLI_assert(brush->sculpt_tool == SCULPT_TOOL_CLOTH); + PBVHNode **nodes = NULL; + + switch (brush->cloth_simulation_area_type) { + case BRUSH_CLOTH_SIMULATION_AREA_LOCAL: { + SculptSearchSphereData data = { + .ss = ss, + .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)), + .original = false, + .ignore_fully_ineffective = false, + .center = ss->cache->initial_location, + }; + BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); + } break; + case BRUSH_CLOTH_SIMULATION_AREA_GLOBAL: + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, r_totnode); + break; + case BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC: { + SculptSearchSphereData data = { + .ss = ss, + .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)), + .original = false, + .ignore_fully_ineffective = false, + .center = ss->cache->location, + }; + BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); + } break; + } + + return nodes; +} + static float cloth_brush_simulation_falloff_get(const Brush *brush, const float radius, const float location[3], diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index ad42750bb92..1fba958d695 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -41,6 +41,7 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_mesh.h" +#include "BKE_mesh_fair.h" #include "BKE_mesh_mapping.h" #include "BKE_multires.h" #include "BKE_node.h" @@ -1024,6 +1025,8 @@ typedef enum eSculptFaceSetEditMode { SCULPT_FACE_SET_EDIT_GROW = 0, SCULPT_FACE_SET_EDIT_SHRINK = 1, SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2, + SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3, + SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4, } eSculptFaceSetEditMode; static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { @@ -1048,6 +1051,22 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { "Delete Geometry", "Deletes the faces that are assigned to the Face Set", }, + { + SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, + "FAIR_POSITIONS", + 0, + "Fair Positions", + "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " + "vertex positions", + }, + { + SCULPT_FACE_SET_EDIT_FAIR_TANGENCY, + "FAIR_TANGENCY", + 0, + "Fair Tangency", + "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " + "vertex tangents", + }, {0, NULL, 0, NULL, NULL}, }; @@ -1181,6 +1200,29 @@ static void sculpt_face_set_delete_geometry(Object *ob, BM_mesh_free(bm); } +static void sculpt_face_set_edit_fair_face_set(Object *ob, + const int active_face_set_id, + const int fair_order) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + Mesh *mesh = ob->data; + bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices"); + + SCULPT_boundary_info_ensure(ob); + + for (int i = 0; i < totvert; i++) { + fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) && + SCULPT_vertex_has_face_set(ss, i, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, i); + } + + MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order); + MEM_freeN(fair_vertices); +} + static void sculpt_face_set_apply_edit(Object *ob, const int active_face_set_id, const int mode, @@ -1204,6 +1246,12 @@ static void sculpt_face_set_apply_edit(Object *ob, case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden); break; + case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS: + sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION); + break; + case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY: + sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY); + break; } } @@ -1230,6 +1278,16 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, return false; } } + + if (ELEM(mode, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY)) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + /* TODO: Multires topology representation using grids and duplicates can't be used directly + * by the fair algorithm. Multires topology needs to be exposed in a different way or + * converted to a mesh for this operation. */ + return false; + } + } + return true; } @@ -1287,11 +1345,38 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, MEM_freeN(nodes); } +static void sculpt_face_set_edit_modify_coordinates(bContext *C, + Object *ob, + const int active_face_set, + const eSculptFaceSetEditMode mode) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; + PBVH *pbvh = ss->pbvh; + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + SCULPT_undo_push_begin(ob, "face set edit"); + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_update(nodes[i]); + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS); + } + sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false); + + if (ss->deform_modifiers_active || ss->shapekey_active) { + SCULPT_flush_stroke_deform(sd, ob, true); + } + SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); + SCULPT_undo_push_end(); + MEM_freeN(nodes); +} + static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); const int mode = RNA_enum_get(op->ptr, "mode"); const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden"); @@ -1320,6 +1405,10 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven case SCULPT_FACE_SET_EDIT_SHRINK: sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden); break; + case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS: + case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY: + sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode); + break; } SCULPT_tag_update_overlays(C); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e0d413bc75f..d1e17c7e59b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -39,7 +39,6 @@ struct AutomaskingCache; struct KeyBlock; struct Object; -struct SculptPoseIKChainSegment; struct SculptUndoNode; struct bContext; @@ -423,6 +422,10 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr, const float outline_col[3], float outline_alpha); +PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss, + Brush *brush, + int *r_totnode); + BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) { return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type, @@ -923,6 +926,10 @@ typedef struct StrokeCache { float (*prev_colors)[4]; + /* Multires Displacement Smear. */ + float (*prev_displacement)[3]; + float (*limit_surface_co)[3]; + /* The rest is temporary storage that isn't saved as a property */ bool first_time; /* Beginning of stroke may do some things special */ diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 3a2b8cf0115..c1f29231f96 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -826,6 +826,11 @@ int /*eContextResult*/ buttons_context(const bContext *C, bContextDataResult *result) { SpaceProperties *sbuts = CTX_wm_space_properties(C); + if (sbuts && sbuts->path == NULL) { + /* path is cleared for SCREEN_OT_redo_last, when global undo does a file-read which clears the + * path (see lib_link_workspace_layout_restore). */ + buttons_context_compute(C, sbuts); + } ButsContextPath *path = sbuts ? sbuts->path : NULL; if (!path) { diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index 0a0846cf216..74e7bc11c26 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -35,7 +35,6 @@ struct bContext; struct bContextDataResult; struct bNode; struct bNodeTree; -struct uiLayout; struct wmOperatorType; struct SpaceProperties_Runtime { diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index afa1fa0edee..d66219c7549 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1178,7 +1178,7 @@ ImBuf *filelist_geticon_image(struct FileList *filelist, const int index) return filelist_geticon_image_ex(file); } -static int filelist_geticon_ex(FileDirEntry *file, +static int filelist_geticon_ex(const FileDirEntry *file, const char *root, const bool is_main, const bool ignore_libdir) @@ -1217,7 +1217,7 @@ static int filelist_geticon_ex(FileDirEntry *file, if (file->redirection_path) { target = file->redirection_path; } - else { + else if (root) { BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath); BLI_path_slash_ensure(fullpath); } @@ -1301,6 +1301,12 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m return filelist_geticon_ex(file, filelist->filelist.root, is_main, false); } +int ED_file_icon(const FileDirEntry *file) +{ + return file->preview_icon_id ? file->preview_icon_id : + filelist_geticon_ex(file, NULL, false, false); +} + /* ********** Main ********** */ static void parent_dir_until_exists_or_default_root(char *dir) @@ -1511,7 +1517,9 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat * in case user switch to a bigger preview size. */ ImBuf *imbuf = IMB_thumb_manage(preview->path, THB_LARGE, source); IMB_thumb_path_unlock(preview->path); - preview->icon_id = BKE_icon_imbuf_create(imbuf); + if (imbuf) { + preview->icon_id = BKE_icon_imbuf_create(imbuf); + } done = true; } @@ -3450,22 +3458,21 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); return; } - else { - /* setup job */ - wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - CTX_data_scene(C), - "Listing Dirs...", - WM_JOB_PROGRESS, - WM_JOB_TYPE_FILESEL_READDIR); - WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free); - WM_jobs_timer(wm_job, 0.01, NC_SPACE | ND_SPACE_FILE_LIST, NC_SPACE | ND_SPACE_FILE_LIST); - WM_jobs_callbacks( - wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob); - - /* start the job */ - WM_jobs_start(CTX_wm_manager(C), wm_job); - } + + /* setup job */ + wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + CTX_data_scene(C), + "Listing Dirs...", + WM_JOB_PROGRESS, + WM_JOB_TYPE_FILESEL_READDIR); + WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free); + WM_jobs_timer(wm_job, 0.01, NC_SPACE | ND_SPACE_FILE_LIST, NC_SPACE | ND_SPACE_FILE_LIST); + WM_jobs_callbacks( + wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob); + + /* start the job */ + WM_jobs_start(CTX_wm_manager(C), wm_job); } void filelist_readjob_stop(wmWindowManager *wm, Scene *owner_scene) diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 59bd5bb50d7..16984bb6e43 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -29,8 +29,8 @@ extern "C" { struct BlendHandle; struct FileList; -struct FileSelection; struct FileSelectAssetLibraryUID; +struct FileSelection; struct wmWindowManager; struct FileDirEntry; diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index cd27b7b5773..b919a30e6cd 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -490,7 +490,7 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile) wmOperator *op = sfile->op; UserDef_FileSpaceData *sfile_udata = &U.file_space_data; - BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_FILES); + sfile->browse_mode = FILE_BROWSE_MODE_FILES; FileSelectParams *params = fileselect_ensure_updated_file_params(sfile); if (!op) { diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 09b7e5b348c..774dc54700c 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -779,7 +779,7 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C), RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items); } -const char *file_context_dir[] = {"active_file", "active_id", NULL}; +static const char *file_context_dir[] = {"active_file", "active_id", NULL}; static int /*eContextResult*/ file_context(const bContext *C, const char *member, @@ -803,16 +803,25 @@ static int /*eContextResult*/ file_context(const bContext *C, if (CTX_data_equals(member, "active_file")) { FileDirEntry *file = filelist_file(sfile->files, params->active_file); + if (file == NULL) { + return CTX_RESULT_NO_DATA; + } + CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); return CTX_RESULT_OK; } - if (CTX_data_equals(member, "active_id")) { + if (CTX_data_equals(member, "id")) { const FileDirEntry *file = filelist_file(sfile->files, params->active_file); + if (file == NULL) { + return CTX_RESULT_NO_DATA; + } ID *id = filelist_file_get_id(file); - if (id) { - CTX_data_id_pointer_set(result, id); + if (id == NULL) { + return CTX_RESULT_NO_DATA; } + + CTX_data_id_pointer_set(result, id); return CTX_RESULT_OK; } diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index f8c71791054..45f3b6cf9c9 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3208,6 +3208,20 @@ static void node_geometry_buts_attribute_mix(uiLayout *layout, uiItemR(col, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("B"), ICON_NONE); } +static void node_geometry_buts_attribute_point_distribute(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribute_method", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_geometry_buts_attribute_color_ramp(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiTemplateColorRamp(layout, ptr, "color_ramp", 0); +} + static void node_geometry_set_butfunc(bNodeType *ntype) { switch (ntype->type) { @@ -3220,7 +3234,7 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_TRIANGULATE: ntype->draw_buttons = node_geometry_buts_triangulate; break; - case GEO_NODE_RANDOM_ATTRIBUTE: + case GEO_NODE_ATTRIBUTE_RANDOMIZE: ntype->draw_buttons = node_geometry_buts_random_attribute; break; case GEO_NODE_ATTRIBUTE_MATH: @@ -3235,6 +3249,12 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_ATTRIBUTE_MIX: ntype->draw_buttons = node_geometry_buts_attribute_mix; break; + case GEO_NODE_POINT_DISTRIBUTE: + ntype->draw_buttons = node_geometry_buts_attribute_point_distribute; + break; + case GEO_NODE_ATTRIBUTE_COLOR_RAMP: + ntype->draw_buttons = node_geometry_buts_attribute_color_ramp; + break; } } diff --git a/source/blender/editors/space_outliner/outliner_context.c b/source/blender/editors/space_outliner/outliner_context.c index e7dc2780c37..a314a640e42 100644 --- a/source/blender/editors/space_outliner/outliner_context.c +++ b/source/blender/editors/space_outliner/outliner_context.c @@ -50,7 +50,7 @@ static void outliner_context_selected_ids(const SpaceOutliner *space_outliner, CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } -const char *outliner_context_dir[] = {"selected_ids", NULL}; +static const char *outliner_context_dir[] = {"selected_ids", NULL}; int /*eContextResult*/ outliner_context(const bContext *C, const char *member, diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index ccb481197e4..339cc3068d0 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -32,7 +32,6 @@ extern "C" { /* internal exports only */ struct ARegion; -struct bContextDataResult; struct EditBone; struct ID; struct ListBase; @@ -43,6 +42,7 @@ struct TreeElement; struct TreeStoreElem; struct ViewLayer; struct bContext; +struct bContextDataResult; struct bPoseChannel; struct wmKeyConfig; struct wmOperatorType; diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index e4c3ebfdff5..492fc5c23bc 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -737,7 +737,7 @@ static void id_local_fn(bContext *C, } static void object_proxy_to_override_convert_fn(bContext *C, - ReportList *UNUSED(reports), + ReportList *reports, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), @@ -754,8 +754,15 @@ static void object_proxy_to_override_convert_fn(bContext *C, return; } - BKE_lib_override_library_proxy_convert( - CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy); + if (!BKE_lib_override_library_proxy_convert( + CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) { + BKE_reportf( + reports, + RPT_ERROR_INVALID_INPUT, + "Could not create a library override from proxy '%s' (might use already local data?)", + ob_proxy->id.name + 2); + return; + } DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_WINDOW, NULL); diff --git a/source/blender/editors/space_outliner/tree/tree_element_nla.hh b/source/blender/editors/space_outliner/tree/tree_element_nla.hh index 3ca62b13bd8..c94287ce576 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_nla.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_nla.hh @@ -23,7 +23,6 @@ #include "tree_element.hh" struct NlaTrack; -struct NlaStrip; namespace blender::ed::outliner { diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 37dfcdbc765..71433a6978a 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -81,9 +81,18 @@ typedef struct SequencerAddData { #define SEQPROP_ENDFRAME (1 << 1) #define SEQPROP_NOPATHS (1 << 2) #define SEQPROP_NOCHAN (1 << 3) +#define SEQPROP_FIT_METHOD (1 << 4) #define SELECT 1 +static const EnumPropertyItem scale_fit_methods[] = { + {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"}, + {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"}, + {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"}, + {SEQ_USE_ORIGINAL_SIZE, "ORIGINAL", 0, "Use Original Size", "Keep image at its original size"}, + {0, NULL, 0, NULL, NULL}, +}; + static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) { PropertyRNA *prop; @@ -123,6 +132,15 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) prop = RNA_def_boolean( ot->srna, "overlap", 0, "Allow Overlap", "Don't correct overlap on new sequence strips"); RNA_def_property_flag(prop, PROP_HIDDEN); + + if (flag & SEQPROP_FIT_METHOD) { + ot->prop = RNA_def_enum(ot->srna, + "fit_method", + scale_fit_methods, + SEQ_SCALE_TO_FIT, + "Fit Method", + "Scale fit method"); + } } static void sequencer_generic_invoke_path__internal(bContext *C, @@ -206,6 +224,8 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato seq_load->end_frame = seq_load->start_frame; seq_load->channel = RNA_int_get(op->ptr, "channel"); seq_load->len = 1; + seq_load->fit_method = RNA_enum_get(op->ptr, "fit_method"); + SEQ_tool_settings_fit_method_set(CTX_data_scene(C), seq_load->fit_method); if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) { /* Full path, file is set by the caller. */ @@ -659,6 +679,7 @@ static int sequencer_add_movie_strip_invoke(bContext *C, if (ed && ed->seqbasep && ed->seqbasep->first) { RNA_boolean_set(op->ptr, "use_framerate", false); } + RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene)); /* This is for drag and drop. */ if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) || @@ -725,7 +746,7 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME); + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD); RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie"); RNA_def_boolean(ot->srna, "use_framerate", @@ -928,6 +949,9 @@ static int sequencer_add_image_strip_invoke(bContext *C, PropertyRNA *prop; Scene *scene = CTX_data_scene(C); + const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method); + /* Name set already by drag and drop. */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { sequencer_generic_invoke_xy__internal( @@ -972,7 +996,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); + sequencer_generic_props__internal(ot, + SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD); RNA_def_boolean(ot->srna, "use_placeholders", diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 081714991ff..d7d601a3c76 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -227,16 +227,16 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. * \param stepsize: The width of a pixel. */ -static void draw_seq_waveform(View2D *v2d, - const bContext *C, - SpaceSeq *sseq, - Scene *scene, - Sequence *seq, - float x1, - float y1, - float x2, - float y2, - float stepsize) +static void draw_seq_waveform_overlay(View2D *v2d, + const bContext *C, + SpaceSeq *sseq, + Scene *scene, + Sequence *seq, + float x1, + float y1, + float x2, + float y2, + float stepsize) { /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ int x1_offset = max_ff(v2d->cur.xmin, x1); @@ -603,121 +603,110 @@ static void draw_seq_outline(Sequence *seq, } } -/* Draw info text on a sequence strip. */ -static void draw_seq_text(View2D *v2d, - Sequence *seq, - SpaceSeq *sseq, - float x1, - float x2, - float y1, - float y2, - bool seq_active, - bool y_threshold) +static const char *draw_seq_text_get_name(Sequence *seq) { - rctf rect; - char str[32 + FILE_MAX]; - size_t str_len; const char *name = seq->name + 2; - uchar col[4]; - - /* All strings should include name. */ if (name[0] == '\0') { name = BKE_sequence_give_name(seq); } + return name; +} - if (ELEM(seq->type, SEQ_TYPE_META, SEQ_TYPE_ADJUSTMENT)) { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); +static void draw_seq_text_get_source(Sequence *seq, char *r_source, size_t source_len) +{ + /* Set source for the most common types. */ + if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) { + BLI_snprintf(r_source, source_len, "%s%s", seq->strip->dir, seq->strip->stripdata->name); } - else if (seq->type == SEQ_TYPE_SCENE) { - if (seq->scene) { - if (seq->scene_camera) { - str_len = BLI_snprintf(str, - sizeof(str), - "%s: %s (%s) | %d", - name, - seq->scene->id.name + 2, - ((ID *)seq->scene_camera)->name + 2, - seq->len); - } - else { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->scene->id.name + 2, seq->len); - } - } - else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + else if (seq->type == SEQ_TYPE_SOUND_RAM) { + if (seq->sound) { + BLI_snprintf(r_source, source_len, "%s", seq->sound->filepath); } } - else if (seq->type == SEQ_TYPE_MOVIECLIP) { - if (seq->clip && !STREQ(name, seq->clip->id.name + 2)) { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->clip->id.name + 2, seq->len); - } - else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); - } + else if (seq->type == SEQ_TYPE_MULTICAM) { + BLI_snprintf(r_source, source_len, "Channel: %d", seq->multicam_source); } - else if (seq->type == SEQ_TYPE_MASK) { - if (seq->mask && !STREQ(name, seq->mask->id.name + 2)) { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->mask->id.name + 2, seq->len); + else if (seq->type == SEQ_TYPE_TEXT) { + TextVars *textdata = seq->effectdata; + BLI_snprintf(r_source, source_len, "%s", textdata->text); + } + else if (seq->type == SEQ_TYPE_SCENE) { + if (seq->scene_camera) { + BLI_snprintf(r_source, + source_len, + "%s (%s)", + seq->scene->id.name + 2, + ((ID *)seq->scene_camera)->name + 2); } else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + BLI_snprintf(r_source, source_len, "%s", seq->scene->id.name + 2); } } - else if (seq->type == SEQ_TYPE_MULTICAM) { - str_len = BLI_snprintf(str, sizeof(str), "Cam %s: %d", name, seq->multicam_source); - } - else if (seq->type == SEQ_TYPE_IMAGE) { - str_len = BLI_snprintf(str, - sizeof(str), - "%s: %s%s | %d", - name, - seq->strip->dir, - seq->strip->stripdata->name, - seq->len); + else if (seq->type == SEQ_TYPE_MOVIECLIP) { + BLI_snprintf(r_source, source_len, "%s", seq->clip->id.name + 2); } - else if (seq->type == SEQ_TYPE_TEXT) { - TextVars *textdata = seq->effectdata; - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", textdata->text, seq->startdisp); + else if (seq->type == SEQ_TYPE_MASK) { + BLI_snprintf(r_source, source_len, "%s", seq->mask->id.name + 2); } - else if (seq->type & SEQ_TYPE_EFFECT) { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + else { + *r_source = '\0'; } - else if (seq->type == SEQ_TYPE_SOUND_RAM) { - /* If a waveform is drawn, avoid to draw text when there is not enough vertical space. */ - if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 && - ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { +} - str[0] = 0; - str_len = 0; - } - else if (seq->sound) { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->sound->filepath, seq->len); +static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq, + Sequence *seq, + char *r_overlay_string, + size_t overlay_string_len) +{ + const char *name = draw_seq_text_get_name(seq); + char source[FILE_MAX]; + int strip_duration = seq->enddisp - seq->startdisp; + draw_seq_text_get_source(seq, source, sizeof(source)); + + bool show_name = sseq->flag & SEQ_SHOW_STRIP_NAME; + bool show_source = (sseq->flag & (SEQ_SHOW_STRIP_SOURCE)) && source[0] != '\0'; + bool show_duration = sseq->flag & SEQ_SHOW_STRIP_DURATION; + + size_t string_len = 0; + if (show_name) { + string_len = BLI_snprintf(r_overlay_string, overlay_string_len, "%s", name); + if (show_source || show_duration) { + string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | "); } - else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + } + if (show_source) { + string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, "%s", source); + if (show_duration) { + string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | "); } } - else if (seq->type == SEQ_TYPE_MOVIE) { - str_len = BLI_snprintf(str, - sizeof(str), - "%s: %s%s | %d", - name, - seq->strip->dir, - seq->strip->stripdata->name, - seq->len); + if (show_duration) { + string_len += BLI_snprintf( + r_overlay_string + string_len, overlay_string_len, "%d", strip_duration); } - else { - /* Should never get here!, but might with files from future. */ - BLI_assert(0); + return string_len; +} + +/* Draw info text on a sequence strip. */ +static void draw_seq_text_overlay(View2D *v2d, + Sequence *seq, + SpaceSeq *sseq, + float x1, + float x2, + float y1, + float y2, + bool seq_active) +{ + char overlay_string[FILE_MAX]; + size_t overlay_string_len = draw_seq_text_get_overlay_string( + sseq, seq, overlay_string, sizeof(overlay_string)); - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + if (overlay_string_len == 0) { + return; } /* White text for the active strip. */ + uchar col[4]; col[0] = col[1] = col[2] = seq_active ? 255 : 10; col[3] = 255; @@ -731,15 +720,16 @@ static void draw_seq_text(View2D *v2d, } } + rctf rect; rect.xmin = x1; rect.ymin = y1; rect.xmax = x2; rect.ymax = y2; - UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col); + UI_view2d_text_cache_add_rectf(v2d, &rect, overlay_string, overlay_string_len, col); } -static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, float pixely) +static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint pos, float pixely) { float x1, x2, y1, y2; uchar col[4], blend_col[3]; @@ -988,7 +978,7 @@ static void fcurve_batch_add_verts(GPUVertBuf *vbo, * - Volume for sound strips. * - Opacity for the other types. */ -static void draw_seq_fcurve( +static void draw_seq_fcurve_overlay( Scene *scene, View2D *v2d, Sequence *seq, float x1, float y1, float x2, float y2, float pixelx) { FCurve *fcu; @@ -1085,11 +1075,21 @@ static void draw_seq_strip(const bContext *C, x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; y2 = seq->machine + SEQ_STRIP_OFSTOP; - /* Calculate height needed for drawing text on strip. */ - float text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely); + float text_margin_y; + bool y_threshold; + if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) || + (sseq->flag & SEQ_SHOW_STRIP_DURATION)) { - /* Is there enough space for drawing something else than text? */ - bool y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac; + /* Calculate height needed for drawing text on strip. */ + text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely); + + /* Is there enough space for drawing something else than text? */ + y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac; + } + else { + text_margin_y = y2; + y_threshold = 1; + } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1102,12 +1102,13 @@ static void draw_seq_strip(const bContext *C, } /* Draw strip offsets when flag is enabled or during "solo preview". */ - if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { - if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { - draw_sequence_extensions(scene, seq, pos, pixely); + if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { + if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { + draw_sequence_extensions_overlay(scene, seq, pos, pixely); + } } } - immUnbindProgram(); x1 = seq->startdisp; @@ -1118,24 +1119,24 @@ static void draw_seq_strip(const bContext *C, drawmeta_contents(scene, seq, x1, y1, x2, y2); } - if (sseq->flag & SEQ_SHOW_FCURVES) { - draw_seq_fcurve(scene, v2d, seq, x1, y1, x2, y2, pixelx); + if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_FCURVES)) { + draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx); } /* Draw sound strip waveform. */ - if ((seq->type == SEQ_TYPE_SOUND_RAM) && (sseq->flag & SEQ_NO_WAVEFORMS) == 0) { - draw_seq_waveform(v2d, - C, - sseq, - scene, - seq, - x1, - y_threshold ? y1 + 0.05f : y1, - x2, - y_threshold ? text_margin_y : y2, - BLI_rctf_size_x(®ion->v2d.cur) / region->winx); + if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) && + (sseq->flag & SEQ_NO_WAVEFORMS) == 0) { + draw_seq_waveform_overlay(v2d, + C, + sseq, + scene, + seq, + x1, + y_threshold ? y1 + 0.05f : y1, + x2, + y_threshold ? text_margin_y : y2, + BLI_rctf_size_x(®ion->v2d.cur) / region->winx); } - /* Draw locked state. */ if (seq->flag & SEQ_LOCK) { draw_seq_locked(x1, y1, x2, y2); @@ -1162,11 +1163,21 @@ static void draw_seq_strip(const bContext *C, calculate_seq_text_offsets(v2d, seq, &x1, &x2, pixelx); - /* Don't draw strip if there is not enough vertical or horizontal space. */ - if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) { - /* Depending on the vertical space, draw text on top or in the center of strip. */ - draw_seq_text( - v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active, y_threshold); + /* If a waveform is drawn, avoid drawing text when there is not enough vertical space. */ + if (seq->type == SEQ_TYPE_SOUND_RAM) { + if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 && + ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + return; + } + } + + if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + /* Don't draw strip if there is not enough vertical or horizontal space. */ + if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) { + /* Depending on the vertical space, draw text on top or in the center of strip. */ + draw_seq_text_overlay( + v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active); + } } } @@ -1360,7 +1371,7 @@ static void sequencer_display_size(Scene *scene, float r_viewrect[2]) r_viewrect[0] *= scene->r.xasp / scene->r.yasp; } -static void sequencer_draw_gpencil(const bContext *C) +static void sequencer_draw_gpencil_overlay(const bContext *C) { /* Draw grease-pencil (image aligned). */ ED_annotation_draw_2dimage(C); @@ -1373,7 +1384,9 @@ static void sequencer_draw_gpencil(const bContext *C) } /* Draw content and safety borders borders. */ -static void sequencer_draw_borders(const SpaceSeq *sseq, const View2D *v2d, const Scene *scene) +static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, + const View2D *v2d, + const Scene *scene) { float x1 = v2d->tot.xmin; float y1 = v2d->tot.ymin; @@ -1825,17 +1838,17 @@ void sequencer_draw_preview(const bContext *C, C, scene, region, sseq, ibuf, scope, draw_overlay, draw_backdrop); /* Draw over image. */ - if (sseq->flag & SEQ_SHOW_METADATA) { + if (sseq->flag & SEQ_SHOW_METADATA && sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0); } } - if (show_imbuf) { - sequencer_draw_borders(sseq, v2d, scene); + if (show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + sequencer_draw_borders_overlay(sseq, v2d, scene); } - if (draw_gpencil && show_imbuf) { - sequencer_draw_gpencil(C); + if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + sequencer_draw_gpencil_overlay(C); } #if 0 sequencer_draw_maskedit(C, scene, region, sseq); @@ -2292,7 +2305,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region) UI_view2d_view_ortho(v2d); /* Get timeline bound-box, needed for the scroll-bars. */ - boundbox_seq(scene, &v2d->tot); + SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot); draw_seq_backdrop(v2d); UI_view2d_constant_grid_draw(v2d, FPS); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 953a77d22a6..ddc9ba2e0f6 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -183,117 +183,13 @@ bool sequencer_view_strips_poll(bContext *C) /** \name Remove Gaps Operator * \{ */ -static bool sequence_offset_after_frame(Scene *scene, const int delta, const int timeline_frame) -{ - Sequence *seq; - Editing *ed = BKE_sequencer_editing_get(scene, false); - bool done = false; - TimeMarker *marker; - - /* All strips >= timeline_frame are shifted. */ - - if (ed == NULL) { - return 0; - } - - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->startdisp >= timeline_frame) { - BKE_sequence_translate(scene, seq, delta); - BKE_sequence_calc(scene, seq); - BKE_sequence_invalidate_cache_preprocessed(scene, seq); - done = true; - } - } - - if (!scene->toolsettings->lock_markers) { - for (marker = scene->markers.first; marker; marker = marker->next) { - if (marker->frame >= timeline_frame) { - marker->frame += delta; - } - } - } - - return done; -} - -void boundbox_seq(Scene *scene, rctf *rect) -{ - Sequence *seq; - Editing *ed = BKE_sequencer_editing_get(scene, false); - float min[2], max[2]; - - if (ed == NULL) { - return; - } - - min[0] = SFRA; - max[0] = EFRA + 1; - min[1] = 0.0; - max[1] = 8.0; - - seq = ed->seqbasep->first; - while (seq) { - - if (min[0] > seq->startdisp - 1) { - min[0] = seq->startdisp - 1; - } - if (max[0] < seq->enddisp + 1) { - max[0] = seq->enddisp + 1; - } - if (max[1] < seq->machine + 2) { - max[1] = seq->machine + 2; - } - - seq = seq->next; - } - - rect->xmin = min[0]; - rect->xmax = max[0]; - rect->ymin = min[1]; - rect->ymax = max[1]; -} - static int sequencer_gap_remove_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - rctf rectf; - int timeline_frame, efra, sfra; - bool first = false, done; - bool do_all = RNA_boolean_get(op->ptr, "all"); - - /* Get first and last frame. */ - boundbox_seq(scene, &rectf); - sfra = (int)rectf.xmin; - efra = (int)rectf.xmax; - - /* Check if the current frame has a gap already. */ - for (timeline_frame = CFRA; timeline_frame >= sfra; timeline_frame--) { - if (SEQ_render_evaluate_frame(scene, timeline_frame)) { - first = true; - break; - } - } + const bool do_all = RNA_boolean_get(op->ptr, "all"); + const Editing *ed = BKE_sequencer_editing_get(scene, false); - for (; timeline_frame < efra; timeline_frame++) { - /* There's still no strip to remove a gap for. */ - if (first == false) { - if (SEQ_render_evaluate_frame(scene, timeline_frame)) { - first = true; - } - } - else if (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) { - done = true; - while (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) { - done = sequence_offset_after_frame(scene, -1, timeline_frame); - if (done == false) { - break; - } - } - if (done == false || do_all == false) { - break; - } - } - } + SEQ_edit_remove_gaps(scene, ed->seqbasep, CFRA, do_all); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -330,9 +226,9 @@ void SEQUENCER_OT_gap_remove(struct wmOperatorType *ot) static int sequencer_gap_insert_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - int frames = RNA_int_get(op->ptr, "frames"); - - sequence_offset_after_frame(scene, frames, CFRA); + const int frames = RNA_int_get(op->ptr, "frames"); + const Editing *ed = BKE_sequencer_editing_get(scene, false); + SEQ_offset_after_frame(scene, ed->seqbasep, frames, CFRA); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -2631,7 +2527,7 @@ void ED_sequencer_deselect_all(Scene *scene) SEQ_CURRENT_END; } -static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op)) +static int sequencer_paste_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -2640,8 +2536,25 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op)) int ofs; Sequence *iseq, *iseq_first; + if (BLI_listbase_count(&seqbase_clipboard) == 0) { + BKE_report(op->reports, RPT_INFO, "No strips to paste"); + return OPERATOR_CANCELLED; + } + ED_sequencer_deselect_all(scene); - ofs = scene->r.cfra - seqbase_clipboard_frame; + if (RNA_boolean_get(op->ptr, "keep_offset")) { + ofs = scene->r.cfra - seqbase_clipboard_frame; + } + else { + int min_seq_startdisp = INT_MAX; + LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) { + if (seq->startdisp < min_seq_startdisp) { + min_seq_startdisp = seq->startdisp; + } + } + /* Paste strips after playhead. */ + ofs = scene->r.cfra - min_seq_startdisp; + } /* Copy strips, temporarily restoring pointers to actual data-blocks. This * must happen on the clipboard itself, so that copying does user counting @@ -2689,6 +2602,11 @@ void SEQUENCER_OT_paste(wmOperatorType *ot) /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + PropertyRNA *prop = RNA_def_boolean( + ot->srna, "keep_offset", false, "Keep Offset", "Keep strip offset to playhead when pasting"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -3496,3 +3414,148 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[ } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Strip Transform Operator + * \{ */ + +enum { + STRIP_TRANSFORM_POSITION, + STRIP_TRANSFORM_SCALE, + STRIP_TRANSFORM_ROTATION, + STRIP_TRANSFORM_ALL, +}; + +static const EnumPropertyItem transform_reset_properties[] = { + {STRIP_TRANSFORM_POSITION, "POSITION", 0, "Position", "Reset strip transform location"}, + {STRIP_TRANSFORM_SCALE, "SCALE", 0, "Scale", "Reset strip transform scale"}, + {STRIP_TRANSFORM_ROTATION, "ROTATION", 0, "Rotation", "Reset strip transform rotation"}, + {STRIP_TRANSFORM_ALL, "ALL", 0, "All", "Reset strip transform location, scale and rotation"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sequencer_strip_transform_clear_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const Editing *ed = BKE_sequencer_editing_get(scene, false); + Sequence *seq; + const int property = RNA_enum_get(op->ptr, "property"); + + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) { + StripTransform *transform = seq->strip->transform; + switch (property) { + case STRIP_TRANSFORM_POSITION: + transform->xofs = 0; + transform->yofs = 0; + break; + case STRIP_TRANSFORM_SCALE: + transform->scale_x = 1.0f; + transform->scale_y = 1.0f; + break; + case STRIP_TRANSFORM_ROTATION: + transform->rotation = 0.0f; + break; + case STRIP_TRANSFORM_ALL: + transform->xofs = 0; + transform->yofs = 0; + transform->scale_x = 1.0f; + transform->scale_y = 1.0f; + transform->rotation = 0.0f; + break; + } + BKE_sequence_invalidate_cache_preprocessed(scene, seq); + } + } + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Clear Strip Transform"; + ot->idname = "SEQUENCER_OT_strip_transform_clear"; + ot->description = "Reset image transformation to default value"; + + /* Api callbacks. */ + ot->exec = sequencer_strip_transform_clear_exec; + ot->poll = sequencer_edit_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, + "property", + transform_reset_properties, + STRIP_TRANSFORM_ALL, + "Property", + "Strip transform property to be reset"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Set Fit Operator + * \{ */ + +static const EnumPropertyItem scale_fit_methods[] = { + {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image so fits in preview"}, + {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image so it fills preview completely"}, + {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image so it fills preview"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const Editing *ed = BKE_sequencer_editing_get(scene, false); + Sequence *seq; + const eSeqImageFitMethod fit_method = RNA_enum_get(op->ptr, "fit_method"); + + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) { + const int timeline_frame = CFRA; + StripElem *strip_elem = SEQ_render_give_stripelem(seq, timeline_frame); + + if (strip_elem == NULL) { + continue; + } + + SEQ_set_scale_to_fit(seq, + strip_elem->orig_width, + strip_elem->orig_height, + scene->r.xsch, + scene->r.ysch, + fit_method); + BKE_sequence_invalidate_cache_preprocessed(scene, seq); + } + } + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Strip Transform Set Fit"; + ot->idname = "SEQUENCER_OT_strip_transform_fit"; + + /* Api callbacks. */ + ot->exec = sequencer_strip_transform_fit_exec; + ot->poll = sequencer_edit_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, + "fit_method", + scale_fit_methods, + SEQ_SCALE_TO_FIT, + "Fit Method", + "Scale fit fit_method"); +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 1ea4fb05d53..4c942a83f2b 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -71,7 +71,6 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain, /* sequencer_edit.c */ struct View2D; void seq_rectf(struct Sequence *seq, struct rctf *rectf); -void boundbox_seq(struct Scene *scene, struct rctf *rect); struct Sequence *find_nearest_seq(struct Scene *scene, struct View2D *v2d, int *hand, @@ -145,6 +144,8 @@ void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot); void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot); void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot); /* sequencer_select.c */ void SEQUENCER_OT_select_all(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index bdf6e4ece7f..7bfc8600544 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -81,6 +81,8 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_change_path); WM_operatortype_append(SEQUENCER_OT_set_range_to_strips); + WM_operatortype_append(SEQUENCER_OT_strip_transform_clear); + WM_operatortype_append(SEQUENCER_OT_strip_transform_fit); /* sequencer_select.c */ WM_operatortype_append(SEQUENCER_OT_select_all); diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 75d92d5f00d..e12c43b7804 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -87,8 +87,10 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op) rctf box; const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + Scene *scene = CTX_data_scene(C); + const Editing *ed = BKE_sequencer_editing_get(scene, false); - boundbox_seq(CTX_data_scene(C), &box); + SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box); UI_view2d_smooth_view(C, region, &box, smooth_viewtx); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 45c7bac54f8..2bf4741e4f5 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -99,7 +99,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | - SEQ_ZOOM_TO_FIT; + SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | + SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -706,7 +707,8 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) SpaceSeq *sseq = area->spacedata.first; Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); - const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW)); + const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && + (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)); /* XXX temp fix for wrong setting in sseq->mainb */ if (sseq->mainb == SEQ_DRAW_SEQUENCE) { diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 227330e8524..91bf2bf8aac 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -52,12 +52,12 @@ struct SnapObjectContext; struct TransDataContainer; struct TransInfo; struct TransSnap; -struct TransformOrientation; struct ViewLayer; struct bContext; struct wmEvent; struct wmKeyConfig; struct wmKeyMap; +struct wmOperator; struct wmTimer; /** #TransInfo.redraw */ diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index b753572ea7b..59fcd016020 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -29,12 +29,9 @@ struct FCurve; struct ListBase; struct Object; struct TransData; -struct TransDataContainer; struct TransDataCurveHandleFlags; struct TransInfo; struct bContext; -struct bKinematicConstraint; -struct bPoseChannel; /* transform_convert.c */ void transform_autoik_update(TransInfo *t, short mode); diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 90c1f241338..8597c372537 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -377,6 +377,9 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) else { mul_v3_m3v3(global_dir, t->spacemtx, global_dir); } + if (t->flag & T_2D_EDIT) { + removeAspectRatio(t, global_dir); + } } else { copy_v3_v3(global_dir, t->values); diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index 5bee572c603..db8ec943bfd 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -25,8 +25,6 @@ /* For enum. */ #include "DNA_space_types.h" -struct SnapObjectParams; - bool peelObjectsTransform(struct TransInfo *t, const float mval[2], const bool use_peel_object, diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 6547e42f410..d78758dcc1c 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -67,6 +67,7 @@ #include "ED_object.h" #include "ED_outliner.h" #include "ED_paint.h" +#include "ED_render.h" #include "ED_space_api.h" #include "ED_util.h" @@ -505,9 +506,9 @@ void ED_OT_flush_edits(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; } -static bool lib_id_load_custom_preview_poll(bContext *C) +static bool lib_id_preview_editing_poll(bContext *C) { - const PointerRNA idptr = CTX_data_pointer_get(C, "active_id"); + const PointerRNA idptr = CTX_data_pointer_get(C, "id"); BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type)); const ID *id = idptr.data; @@ -541,7 +542,7 @@ static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - PointerRNA idptr = CTX_data_pointer_get(C, "active_id"); + PointerRNA idptr = CTX_data_pointer_get(C, "id"); ID *id = idptr.data; BKE_previewimg_id_custom_set(id, path); @@ -558,7 +559,7 @@ void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot) ot->idname = "ED_OT_lib_id_load_custom_preview"; /* api callbacks */ - ot->poll = lib_id_load_custom_preview_poll; + ot->poll = lib_id_preview_editing_poll; ot->exec = lib_id_load_custom_preview_exec; ot->invoke = WM_operator_filesel; @@ -573,3 +574,35 @@ void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot) FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); } + +static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PointerRNA idptr = CTX_data_pointer_get(C, "id"); + ID *id = idptr.data; + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + PreviewImage *preview = BKE_previewimg_id_get(id); + if (preview) { + BKE_previewimg_clear(preview); + } + UI_icon_render_id(C, NULL, id, true, true); + + WM_event_add_notifier(C, NC_ASSET, NULL); + + return OPERATOR_FINISHED; +} + +void ED_OT_lib_id_generate_preview(wmOperatorType *ot) +{ + ot->name = "Generate Preview"; + ot->description = "Create an automatic preview for the selected data-block"; + ot->idname = "ED_OT_lib_id_generate_preview"; + + /* api callbacks */ + ot->poll = lib_id_preview_editing_poll; + ot->exec = lib_id_generate_preview_exec; + + /* flags */ + ot->flag = OPTYPE_INTERNAL; +} diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 306f8a2c561..28567234fab 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -25,7 +25,6 @@ struct BMFace; struct BMLoop; -struct Image; struct Object; struct Scene; struct SpaceImage; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h index e5a6d9e6a8f..30e54f44499 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h @@ -23,9 +23,7 @@ #pragma once -struct GHash; struct MDeformVert; -struct Main; struct Material; struct Object; struct bGPDlayer; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c index aa21bf192c4..b8fa88327fc 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c @@ -237,9 +237,17 @@ static void generate_geometry(GpencilModifierData *md, /* To ensure a nice distribution, we use halton sequence and offset using the seed. */ BLI_halton_3d(primes, offset, x, r); - for (int i = 0; i < 3; i++) { - rand[j][i] = fmodf(r[i] * 2.0 - 1.0 + rand_offset, 1.0f); - rand[j][i] = fmodf(sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0f); + if ((mmd->flag & GP_ARRAY_UNIFORM_RANDOM_SCALE) && j == 2) { + float rand_value; + rand_value = fmodf(r[0] * 2.0 - 1.0 + rand_offset, 1.0f); + rand_value = fmodf(sin(rand_value * 12.9898 + j * 78.233) * 43758.5453, 1.0f); + copy_v3_fl(rand[j], rand_value); + } + else { + for (int i = 0; i < 3; i++) { + rand[j][i] = fmodf(r[i] * 2.0 - 1.0 + rand_offset, 1.0f); + rand[j][i] = fmodf(sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0f); + } } } /* Calculate Random matrix. */ @@ -425,6 +433,7 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE); uiItemR(layout, ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE); uiItemR(layout, ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE); + uiItemR(layout, ptr, "use_uniform_random_scale", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE); } diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 27a7ea1e6a5..55716b584c3 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,8 +27,6 @@ extern "C" { #endif -struct GPUTexture; -struct GPUUniformBuf; struct GPUVertBuf; /** Opaque type hidding blender::gpu::Shader */ diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index e9c081abd22..91119bd05a1 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -28,14 +28,6 @@ #include "GPU_state.h" struct GPUVertBuf; -struct ImBuf; -struct Image; -struct ImageUser; -struct MovieClip; -struct MovieClipUser; -struct PreviewImage; - -struct GPUFrameBuffer; /** Opaque type hiding blender::gpu::Texture. */ typedef struct GPUTexture GPUTexture; diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 5d130d75d2c..f3d58a55814 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -31,10 +31,7 @@ extern "C" { struct GPUMaterial; struct GPUNodeGraph; -struct GPUOutput; struct GPUShader; -struct GSet; -struct ListBase; typedef struct GPUPass { struct GPUPass *next; diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 0ef95d94c0d..929191cff73 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -33,8 +33,6 @@ struct GPUNode; struct GPUOutput; -struct GPUShader; -struct GPUVertAttrLayers; struct ListBase; typedef enum eGPUDataSource { diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh index 00d10776864..18ea6d18caf 100644 --- a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh @@ -24,6 +24,8 @@ #include "BLI_sys_types.h" +struct GPUUniformBuf; + namespace blender { namespace gpu { diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 188c8786665..a83654d31e7 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -616,8 +616,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_framebuffer_ensure_config(&dfbl->stereo_comp_fb, { GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color), GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), + GPU_ATTACHMENT_TEXTURE(dtxl->color), }); GPUVertFormat *vert_format = immVertexFormat(); @@ -628,8 +628,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_matrix_identity_set(); GPU_matrix_identity_projection_set(); immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE); - immUniform1i("imageTexture", 0); - immUniform1i("overlayTexture", 1); + immUniform1i("overlayTexture", 0); + immUniform1i("imageTexture", 1); int settings = stereo_format->display_mode; if (settings == S3D_DISPLAY_ANAGLYPH) { switch (stereo_format->anaglyph_type) { diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 957352595ed..58ddc918f61 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -382,6 +382,8 @@ bool IMB_anim_can_produce_frames(const struct anim *anim); int ismovie(const char *filepath); void IMB_anim_set_preseek(struct anim *anim, int preseek); int IMB_anim_get_preseek(struct anim *anim); +int IMB_anim_get_image_width(struct anim *anim); +int IMB_anim_get_image_height(struct anim *anim); /** * diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index d825b20f5f2..9b01ea0840f 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1506,3 +1506,13 @@ int IMB_anim_get_preseek(struct anim *anim) { return anim->preseek; } + +int IMB_anim_get_image_width(struct anim *anim) +{ + return anim->x; +} + +int IMB_anim_get_image_height(struct anim *anim) +{ + return anim->y; +} diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h index a0d9257b822..2098b8b2505 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h @@ -29,7 +29,6 @@ #include <Alembic/Abc/OObject.h> struct Depsgraph; -struct ID; struct Object; namespace blender::io::alembic { diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h index d23e69cf73e..4997e498199 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.h +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h @@ -29,7 +29,6 @@ #include "DNA_material_types.h" struct IDProperty; -struct Material; struct Object; namespace blender::io::alembic { diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h index 3759ffa4310..901fd70601f 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.h +++ b/source/blender/io/alembic/exporter/abc_writer_hair.h @@ -23,9 +23,6 @@ #include <Alembic/AbcGeom/OCurves.h> #include <vector> -struct ParticleSettings; -struct ParticleSystem; - namespace blender::io::alembic { class ABCHairWriter : public ABCAbstractWriter { diff --git a/source/blender/io/alembic/intern/abc_reader_archive.h b/source/blender/io/alembic/intern/abc_reader_archive.h index 2a4fd6bd8fb..67000194aa1 100644 --- a/source/blender/io/alembic/intern/abc_reader_archive.h +++ b/source/blender/io/alembic/intern/abc_reader_archive.h @@ -28,7 +28,6 @@ #include <fstream> struct Main; -struct Scene; namespace blender::io::alembic { diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index e066ca8ba8f..300c555ac8f 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -43,13 +43,11 @@ #include <set> #include <string> -struct Base; struct Depsgraph; struct DupliObject; struct ID; struct Object; struct ParticleSystem; -struct ViewLayer; namespace blender::io { diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h index 41ff34b327e..66e87f0eed1 100644 --- a/source/blender/io/usd/intern/usd_exporter_context.h +++ b/source/blender/io/usd/intern/usd_exporter_context.h @@ -24,7 +24,6 @@ #include <pxr/usd/usd/common.h> struct Depsgraph; -struct Object; namespace blender::io::usd { diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h index 922ab761bd9..cd19becbb07 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -28,7 +28,6 @@ #include <pxr/usd/usd/timeCode.h> struct Depsgraph; -struct ID; struct Object; namespace blender::io::usd { diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index 6a3b8d515dc..2143164e3dd 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -33,7 +33,6 @@ #include "DNA_material_types.h" struct Material; -struct Object; namespace blender::io::usd { diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index b9ea90736ff..40e2d0d8674 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -25,7 +25,6 @@ extern "C" { #endif -struct Scene; struct bContext; struct USDExportParams { diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index af8e83a2c44..265baa16cc5 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -509,6 +509,8 @@ typedef enum ID_Type { #define ID_IS_OVERRIDE_LIBRARY_TEMPLATE(_id) \ (((ID *)(_id))->override_library != NULL && ((ID *)(_id))->override_library->reference == NULL) +#define ID_IS_ASSET(_id) (((const ID *)(_id))->asset_data != NULL) + /* Check whether datablock type is covered by copy-on-write. */ #define ID_TYPE_IS_COW(_id_type) (!ELEM(_id_type, ID_BR, ID_PAL, ID_IM)) diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 96e870cff8e..f12934c9104 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -457,6 +457,7 @@ typedef enum eBrushSculptTool { SCULPT_TOOL_SMEAR = 29, SCULPT_TOOL_BOUNDARY = 30, SCULPT_TOOL_DISPLACEMENT_ERASER = 31, + SCULPT_TOOL_DISPLACEMENT_SMEAR = 32, } eBrushSculptTool; /* Brush.uv_sculpt_tool */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 4b020019062..1709ea5dc63 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -373,7 +373,7 @@ typedef struct Brush { typedef struct tPaletteColorHSV { float rgb[3]; float value; - float h; + float h; float s; float v; } tPaletteColorHSV; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index ae0bb20e529..832d55ea151 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -75,8 +75,7 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we cant use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[50]; - char _pad[4]; + int typemap[51]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ @@ -156,7 +155,9 @@ typedef enum CustomDataType { CD_PROP_FLOAT3 = 48, CD_PROP_FLOAT2 = 49, - CD_NUMTYPES = 50, + CD_PROP_BOOL = 50, + + CD_NUMTYPES = 51, } CustomDataType; /* Bits for CustomDataMask */ @@ -208,6 +209,7 @@ typedef enum CustomDataType { #define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR) #define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3) #define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2) +#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL) /** Multires loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index bd5afc457ac..9ac40495887 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -351,6 +351,7 @@ typedef enum eArrayGpencil_Flag { GP_ARRAY_USE_OFFSET = (1 << 7), GP_ARRAY_USE_RELATIVE = (1 << 8), GP_ARRAY_USE_OB_OFFSET = (1 << 9), + GP_ARRAY_UNIFORM_RANDOM_SCALE = (1 << 10), } eArrayGpencil_Flag; typedef struct BuildGpencilModifierData { diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 212f0bfa1c9..949b0bb5bf5 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -32,8 +32,8 @@ extern "C" { #endif struct AnimData; -struct MDeformVert; struct Curve; +struct MDeformVert; #define GP_DEFAULT_PIX_FACTOR 1.0f #define GP_DEFAULT_GRID_LINES 4 diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 04fbc030ed9..c2337b28e54 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -35,7 +35,6 @@ struct AnimData; struct BVHCache; struct Ipo; struct Key; -struct LinkNode; struct MCol; struct MEdge; struct MFace; @@ -44,7 +43,6 @@ struct MLoopCol; struct MLoopTri; struct MLoopUV; struct MPoly; -struct MPropCol; struct MVert; struct Material; struct Mesh; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index d512d8ffe34..7ad339c66af 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -33,6 +33,7 @@ extern "C" { #endif struct AnimData; +struct Collection; struct ID; struct Image; struct ListBase; @@ -43,7 +44,6 @@ struct bNodePreview; struct bNodeTreeExec; struct bNodeType; struct uiBlock; -struct Collection; #define NODE_MAXSTR 64 @@ -1084,6 +1084,10 @@ typedef struct NodeAttributeMix { uint8_t input_type_b; } NodeAttributeMix; +typedef struct NodeAttributeColorRamp { + ColorBand color_ramp; +} NodeAttributeColorRamp; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1501,6 +1505,11 @@ typedef enum GeometryNodeAttributeInputMode { GEO_NODE_ATTRIBUTE_INPUT_COLOR = 3, } GeometryNodeAttributeInputMode; +typedef enum GeometryNodePointDistributeMethod { + GEO_NODE_POINT_DISTRIBUTE_RANDOM = 0, + GEO_NODE_POINT_DISTRIBUTE_POISSON = 1, +} GeometryNodePointDistributeMethod; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 0a3a2cd0b64..8b13db8a012 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -38,9 +38,8 @@ extern "C" { struct AnimData; struct BoundBox; -struct DerivedMesh; struct FluidsimSettings; -struct GpencilBatchCache; +struct GeometrySet; struct Ipo; struct Material; struct Mesh; @@ -51,7 +50,6 @@ struct RigidBodyOb; struct SculptSession; struct SoftBody; struct bGPdata; -struct GeometrySet; /* Vertex Groups - Name Info */ typedef struct bDeformGroup { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 8d43483dfe4..f73f99eb4e7 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1339,6 +1339,18 @@ typedef struct MeshStatVis { float sharp_min, sharp_max; } MeshStatVis; +typedef struct SequencerToolSettings { + /* eSeqImageFitMethod */ + int fit_method; +} SequencerToolSettings; + +typedef enum eSeqImageFitMethod { + SEQ_SCALE_TO_FIT, + SEQ_SCALE_TO_FILL, + SEQ_STRETCH_TO_FILL, + SEQ_USE_ORIGINAL_SIZE, +} eSeqImageFitMethod; + /* *************************************************************** */ /* Tool Settings */ @@ -1513,6 +1525,9 @@ typedef struct ToolSettings { * Temporary until there is a proper preset system that stores the profiles or maybe stores * entire bevel configurations. */ struct CurveProfile *custom_bevel_profile_preset; + + struct SequencerToolSettings *sequencer_tool_settings; + } ToolSettings; /* *************************************************************** */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 4161faca386..4276e8b568e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -621,6 +621,10 @@ typedef enum eSpaceSeq_Flag { SEQ_SHOW_METADATA = (1 << 10), SEQ_SHOW_MARKERS = (1 << 11), /* show markers region */ SEQ_ZOOM_TO_FIT = (1 << 12), + SEQ_SHOW_STRIP_OVERLAY = (1 << 13), + SEQ_SHOW_STRIP_NAME = (1 << 14), + SEQ_SHOW_STRIP_SOURCE = (1 << 15), + SEQ_SHOW_STRIP_DURATION = (1 << 16), } eSpaceSeq_Flag; /* SpaceSeq.view */ diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 95f6340174a..f98ca47d767 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -44,6 +44,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Float Color", "RGBA color with floating-point precisions"}, {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"}, {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, + {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 08c655d493c..a361feba439 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -134,6 +134,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""}, {SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""}, {SCULPT_TOOL_DISPLACEMENT_ERASER, "DISPLACEMENT_ERASER", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Eraser", ""}, + {SCULPT_TOOL_DISPLACEMENT_SMEAR, "DISPLACEMENT_SMEAR", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Smear", ""}, {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""}, {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""}, {SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""}, diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 5f131de6a40..89eb989a442 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1560,6 +1560,12 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_USE_RELATIVE); RNA_def_property_ui_text(prop, "Shift", "Enable shift"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_UNIFORM_RANDOM_SCALE); + RNA_def_property_ui_text( + prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpencilbuild(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 153e2aebd22..d4ac3d1b084 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -452,6 +452,20 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = { + {GEO_NODE_POINT_DISTRIBUTE_RANDOM, + "RANDOM", + 0, + "Random", + "Distribute points randomly on the surface"}, + {GEO_NODE_POINT_DISTRIBUTE_POISSON, + "POISSON", + 0, + "Poisson Disk", + "Project points on the surface evenly with a Poisson disk distribution"}, + {0, NULL, 0, NULL, NULL}, +}; + #endif #ifdef RNA_RUNTIME @@ -1876,7 +1890,7 @@ static const EnumPropertyItem *itemf_function_check( static bool attribute_random_type_supported(const EnumPropertyItem *item) { - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3); + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL); } static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf( bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) @@ -1898,7 +1912,7 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_domain_itemf( static bool attribute_fill_type_supported(const EnumPropertyItem *item) { - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR); + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL); } static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), @@ -8388,7 +8402,7 @@ static void def_geo_attribute_create_common(StructRNA *srna, RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_random_attribute(StructRNA *srna) +static void def_geo_attribute_randomize(StructRNA *srna) { def_geo_attribute_create_common(srna, "rna_GeometryNodeAttributeRandom_type_itemf", @@ -8481,6 +8495,30 @@ static void def_geo_attribute_mix(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_point_distribute(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_geometry_point_distribute_method_items); + RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM); + RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_attribute_color_ramp(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeAttributeColorRamp", "storage"); + + prop = RNA_def_property(srna, "color_ramp", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ColorRamp"); + RNA_def_property_ui_text(prop, "Color Ramp", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index e149bb4ecad..7cae88d292b 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2200,6 +2200,11 @@ static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.curve_paint_settings"); } +static char *rna_SequencerToolSettings_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.sequencer_tool_settings"); +} + /* generic function to recalc geometry */ static void rna_EditMesh_update(bContext *C, PointerRNA *UNUSED(ptr)) { @@ -3584,6 +3589,38 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "custom_bevel_profile_preset"); RNA_def_property_struct_type(prop, "CurveProfile"); RNA_def_property_ui_text(prop, "Curve Profile Widget", "Used for defining a profile's path"); + + /* Sequencer tool settings */ + prop = RNA_def_property(srna, "sequencer_tool_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SequencerToolSettings"); + RNA_def_property_ui_text(prop, "Sequencer Tool Settings", NULL); +} + +static void rna_def_sequencer_tool_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem scale_fit_methods[] = { + {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"}, + {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"}, + {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"}, + {SEQ_USE_ORIGINAL_SIZE, + "ORIGINAL", + 0, + "Use Original Size", + "Keep image at its original size"}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "SequencerToolSettings", NULL); + RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path"); + RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", ""); + + prop = RNA_def_property(srna, "fit_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, scale_fit_methods); + RNA_def_property_ui_text(prop, "Fit Method", "Scale fit method"); } static void rna_def_unified_paint_settings(BlenderRNA *brna) @@ -7968,6 +8005,7 @@ void RNA_def_scene(BlenderRNA *brna) rna_def_gpencil_interpolate(brna); rna_def_unified_paint_settings(brna); rna_def_curve_paint_settings(brna); + rna_def_sequencer_tool_settings(brna); rna_def_statvis(brna); rna_def_unit_settings(brna); rna_def_scene_image_format_data(brna); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 0e779c8cc97..2f821dad811 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2602,7 +2602,7 @@ static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr) { const FileDirEntry *entry = ptr->data; - return entry->preview_icon_id; + return ED_file_icon(entry); } static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) @@ -5176,7 +5176,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) prop = RNA_def_property(srna, "waveform_display_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, waveform_type_display_items); - RNA_def_property_ui_text(prop, "Waveform Displaying", "How Waveforms are drawn"); + RNA_def_property_ui_text(prop, "Waveform Display", "How Waveforms are drawn"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); prop = RNA_def_property(srna, "use_zoom_to_fit", PROP_BOOLEAN, PROP_NONE); @@ -5229,6 +5229,27 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_FCURVES); RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_OVERLAY); + RNA_def_property_ui_text(prop, "Show Overlay", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_NAME); + RNA_def_property_ui_text(prop, "Show Name", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_source", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_SOURCE); + RNA_def_property_ui_text( + prop, "Show Source", "Display path to source file, or name of source datablock"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_duration", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_DURATION); + RNA_def_property_ui_text(prop, "Show Duration", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_text(BlenderRNA *brna) diff --git a/source/blender/modifiers/MOD_nodes.h b/source/blender/modifiers/MOD_nodes.h index 9c75e7e3416..907dbe9c5f4 100644 --- a/source/blender/modifiers/MOD_nodes.h +++ b/source/blender/modifiers/MOD_nodes.h @@ -17,8 +17,8 @@ #pragma once struct Main; -struct Object; struct NodesModifierData; +struct Object; #ifdef __cplusplus extern "C" { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 11852f6f723..bc0e7972bcb 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -138,8 +138,10 @@ set(SRC function/nodes/node_fn_switch.cc function/node_function_util.cc + geometry/nodes/node_geo_attribute_color_ramp.cc geometry/nodes/node_geo_attribute_fill.cc geometry/nodes/node_geo_attribute_math.cc + geometry/nodes/node_geo_attribute_randomize.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_edge_split.cc @@ -147,8 +149,8 @@ set(SRC geometry/nodes/node_geo_attribute_mix.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc + geometry/nodes/node_geo_point_distribute_poisson_disk.cc geometry/nodes/node_geo_point_instance.cc - geometry/nodes/node_geo_random_attribute.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index e3ec0451832..f1cd55ce048 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -35,10 +35,11 @@ void register_node_type_geo_triangulate(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); void register_node_type_geo_object_info(void); -void register_node_type_geo_random_attribute(void); +void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_attribute_mix(void); +void register_node_type_geo_attribute_color_ramp(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 5fe554e0478..445e1ed6af2 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -26,6 +26,8 @@ namespace blender::nodes { +using bke::BooleanReadAttribute; +using bke::BooleanWriteAttribute; using bke::Color4fReadAttribute; using bke::Color4fWriteAttribute; using bke::Float3ReadAttribute; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 491f49cf425..662952abb59 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -271,14 +271,15 @@ DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Spl DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") -DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") +DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "") -DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index ec389961615..c97463cdc22 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -42,4 +42,10 @@ namespace blender::nodes { void update_attribute_input_socket_availabilities(bNode &node, const StringRef name, const GeometryNodeAttributeInputMode mode); -} + +void poisson_disk_point_elimination(Vector<float3> const *input_points, + Vector<float3> *output_points, + float maximum_distance, + float3 boundbox); + +} // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc new file mode 100644 index 00000000000..3f7023ba88f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#include "node_geometry_util.hh" + +#include "BKE_colorband.h" + +static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_color_ramp_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) +{ + const bNode &bnode = params.node(); + NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)bnode.storage; + + const std::string result_name = params.get_input<std::string>("Result"); + /* Once we support more domains at the user level, we have to decide how the result domain is + * choosen. */ + const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + const CustomDataType result_type = CD_PROP_COLOR; + + WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write( + result_name, result_domain, result_type); + if (!attribute_result) { + return; + } + + Color4fWriteAttribute attribute_out = std::move(attribute_result); + + const std::string input_name = params.get_input<std::string>("Attribute"); + FloatReadAttribute attribute_in = component.attribute_get_for_read<float>( + input_name, result_domain, 0.0f); + + Span<float> data_in = attribute_in.get_span(); + MutableSpan<Color4f> data_out = attribute_out.get_span(); + + ColorBand *color_ramp = &node_storage->color_ramp; + for (const int i : data_in.index_range()) { + BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]); + } + + attribute_out.apply_span(); +} + +static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + if (geometry_set.has<MeshComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); + } + if (geometry_set.has<PointCloudComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN( + sizeof(NodeAttributeColorRamp), __func__); + BKE_colorband_init(&node_storage->color_ramp, true); + node->storage = node_storage; +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_color_ramp() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_color_ramp_in, geo_node_attribute_color_ramp_out); + node_type_storage( + &ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_color_ramp_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index d3c7e86b708..5cdbd18ecc8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -27,6 +27,7 @@ static bNodeSocketTemplate geo_node_attribute_fill_in[] = { {SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {-1, ""}, }; @@ -45,12 +46,14 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *socket_value_float = socket_value_vector->next; bNodeSocket *socket_value_color4f = socket_value_float->next; + bNodeSocket *socket_value_boolean = socket_value_color4f->next; const CustomDataType data_type = static_cast<CustomDataType>(node->custom1); nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3); nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT); nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); } namespace blender::nodes { @@ -96,6 +99,14 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams color4f_attribute.apply_span(); break; } + case CD_PROP_BOOL: { + BooleanWriteAttribute boolean_attribute = std::move(attribute); + const bool value = params.get_input<bool>("Value_003"); + MutableSpan<bool> attribute_span = boolean_attribute.get_span(); + attribute_span.fill(value); + boolean_attribute.apply_span(); + break; + } default: break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 5cacb96412c..53df2e8c087 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -21,7 +21,7 @@ #include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" -static bNodeSocketTemplate geo_node_random_attribute_in[] = { +static bNodeSocketTemplate geo_node_attribute_randomize_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_STRING, N_("Attribute")}, {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, @@ -32,17 +32,17 @@ static bNodeSocketTemplate geo_node_random_attribute_in[] = { {-1, ""}, }; -static bNodeSocketTemplate geo_node_random_attribute_out[] = { +static bNodeSocketTemplate geo_node_attribute_randomize_out[] = { {SOCK_GEOMETRY, N_("Geometry")}, {-1, ""}, }; -static void geo_node_random_attribute_init(bNodeTree *UNUSED(tree), bNode *node) +static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) { node->custom1 = CD_PROP_FLOAT; } -static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *node) +static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sock_max_vector = sock_min_vector->next; @@ -59,6 +59,16 @@ static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *no namespace blender::nodes { +static void randomize_attribute(BooleanWriteAttribute &attribute, RandomNumberGenerator &rng) +{ + MutableSpan<bool> attribute_span = attribute.get_span(); + for (const int i : IndexRange(attribute.size())) { + const bool value = rng.get_float() > 0.5f; + attribute_span[i] = value; + } + attribute.apply_span(); +} + static void randomize_attribute(FloatWriteAttribute &attribute, float min, float max, @@ -121,6 +131,11 @@ static void randomize_attribute(GeometryComponent &component, randomize_attribute(float3_attribute, min_value, max_value, rng); break; } + case CD_PROP_BOOL: { + BooleanWriteAttribute boolean_attribute = std::move(attribute); + randomize_attribute(boolean_attribute, rng); + break; + } default: break; } @@ -147,15 +162,16 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_random_attribute() +void register_node_type_geo_attribute_randomize() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out); - node_type_init(&ntype, geo_node_random_attribute_init); - node_type_update(&ntype, geo_node_random_attribute_update); + &ntype, GEO_NODE_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_randomize_in, geo_node_attribute_randomize_out); + node_type_init(&ntype, geo_node_attribute_randomize_init); + node_type_update(&ntype, geo_node_attribute_randomize_update); ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 2f5f7e264bc..8be9636e14d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -24,6 +24,7 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BKE_bvhutils.h" #include "BKE_deform.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -33,8 +34,10 @@ static bNodeSocketTemplate geo_node_point_distribute_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_FLOAT, N_("Density"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Minimum Distance"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Maximum Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE}, {SOCK_STRING, N_("Density Attribute")}, + {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, {-1, ""}, }; @@ -43,11 +46,19 @@ static bNodeSocketTemplate geo_node_point_distribute_out[] = { {-1, ""}, }; +static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + + nodeSetSocketAvailability(sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON)); +} + namespace blender::nodes { -static Vector<float3> scatter_points_from_mesh(const Mesh *mesh, - const float density, - const FloatReadAttribute &density_factors) +static Vector<float3> random_scatter_points_from_mesh(const Mesh *mesh, + const float density, + const FloatReadAttribute &density_factors, + const int seed) { /* This only updates a cache and can be considered to be logically const. */ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh)); @@ -71,7 +82,7 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh, 3.0f; const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); - const int looptri_seed = BLI_hash_int(looptri_index); + const int looptri_seed = BLI_hash_int(looptri_index + seed); RandomNumberGenerator looptri_rng(looptri_seed); const float points_amount_fl = area * density * looptri_density_factor; @@ -90,17 +101,158 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh, return points; } +struct RayCastAll_Data { + void *bvhdata; + + BVHTree_RayCastCallback raycast_callback; + + const Mesh *mesh; + float base_weight; + FloatReadAttribute *density_factors; + Vector<float3> *projected_points; + float cur_point_weight; +}; + +static void project_2d_bvh_callback(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + struct RayCastAll_Data *data = (RayCastAll_Data *)userdata; + data->raycast_callback(data->bvhdata, index, ray, hit); + if (hit->index != -1) { + /* This only updates a cache and can be considered to be logically const. */ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(data->mesh)); + const MVert *mvert = data->mesh->mvert; + + const MLoopTri &looptri = looptris[index]; + const FloatReadAttribute &density_factors = data->density_factors[0]; + + const int v0_index = data->mesh->mloop[looptri.tri[0]].v; + const int v1_index = data->mesh->mloop[looptri.tri[1]].v; + const int v2_index = data->mesh->mloop[looptri.tri[2]].v; + + const float v0_density_factor = std::max(0.0f, density_factors[v0_index]); + const float v1_density_factor = std::max(0.0f, density_factors[v1_index]); + const float v2_density_factor = std::max(0.0f, density_factors[v2_index]); + + /* Calculate barycentric weights for hit point. */ + float3 weights; + interp_weights_tri_v3( + weights, mvert[v0_index].co, mvert[v1_index].co, mvert[v2_index].co, hit->co); + + float point_weight = weights[0] * v0_density_factor + weights[1] * v1_density_factor + + weights[2] * v2_density_factor; + + point_weight *= data->base_weight; + + if (point_weight >= FLT_EPSILON && data->cur_point_weight <= point_weight) { + data->projected_points->append(hit->co); + } + } +} + +static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh, + const float density, + const float minimum_distance, + const FloatReadAttribute &density_factors, + const int seed) +{ + Vector<float3> points; + + if (minimum_distance <= FLT_EPSILON || density <= FLT_EPSILON) { + return points; + } + + /* Scatter points randomly on the mesh with higher density (5-7) times higher than desired for + * good quality possion disk distributions. */ + int quality = 5; + const int output_points_target = 1000; + points.resize(output_points_target * quality); + + const float required_area = output_points_target * + (2.0f * sqrtf(3.0f) * minimum_distance * minimum_distance); + const float point_scale_multiplier = sqrtf(required_area); + + { + const int rnd_seed = BLI_hash_int(seed); + RandomNumberGenerator point_rng(rnd_seed); + + for (int i = 0; i < points.size(); i++) { + points[i].x = point_rng.get_float() * point_scale_multiplier; + points[i].y = point_rng.get_float() * point_scale_multiplier; + points[i].z = 0.0f; + } + } + + /* Eliminate the scattered points until we get a possion distribution. */ + Vector<float3> output_points(output_points_target); + + const float3 bounds_max = float3(point_scale_multiplier, point_scale_multiplier, 0); + poisson_disk_point_elimination(&points, &output_points, 2.0f * minimum_distance, bounds_max); + Vector<float3> final_points; + final_points.reserve(output_points_target); + + /* Check if we have any points we should remove from the final possion distribition. */ + BVHTreeFromMesh treedata; + BKE_bvhtree_from_mesh_get(&treedata, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 2); + + float3 bb_min, bb_max; + BLI_bvhtree_get_bounding_box(treedata.tree, bb_min, bb_max); + + struct RayCastAll_Data data; + data.bvhdata = &treedata; + data.raycast_callback = treedata.raycast_callback; + data.mesh = mesh; + data.projected_points = &final_points; + data.density_factors = const_cast<FloatReadAttribute *>(&density_factors); + data.base_weight = std::min( + 1.0f, density / (output_points.size() / (point_scale_multiplier * point_scale_multiplier))); + + const float max_dist = bb_max[2] - bb_min[2] + 2.0f; + const float3 dir = float3(0, 0, -1); + float3 raystart; + raystart.z = bb_max[2] + 1.0f; + + float tile_start_x_coord = bb_min[0]; + int tile_repeat_x = ceilf((bb_max[0] - bb_min[0]) / point_scale_multiplier); + + float tile_start_y_coord = bb_min[1]; + int tile_repeat_y = ceilf((bb_max[1] - bb_min[1]) / point_scale_multiplier); + + for (int x = 0; x < tile_repeat_x; x++) { + float tile_curr_x_coord = x * point_scale_multiplier + tile_start_x_coord; + for (int y = 0; y < tile_repeat_y; y++) { + float tile_curr_y_coord = y * point_scale_multiplier + tile_start_y_coord; + for (int idx = 0; idx < output_points.size(); idx++) { + raystart.x = output_points[idx].x + tile_curr_x_coord; + raystart.y = output_points[idx].y + tile_curr_y_coord; + + data.cur_point_weight = (float)idx / (float)output_points.size(); + + BLI_bvhtree_ray_cast_all( + treedata.tree, raystart, dir, 0.0f, max_dist, project_2d_bvh_callback, &data); + } + } + } + + return final_points; +} + static void geo_node_point_distribute_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; + GeometryNodePointDistributeMethod distribute_method = + static_cast<GeometryNodePointDistributeMethod>(params.node().custom1); + if (!geometry_set.has_mesh()) { params.set_output("Geometry", std::move(geometry_set_out)); return; } - const float density = params.extract_input<float>("Density"); + const float density = params.extract_input<float>("Maximum Density"); const std::string density_attribute = params.extract_input<std::string>("Density Attribute"); if (density <= 0.0f) { @@ -113,8 +265,19 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>( density_attribute, ATTR_DOMAIN_POINT, 1.0f); + const int seed = params.get_input<int>("Seed"); - Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors); + Vector<float3> points; + + switch (distribute_method) { + case GEO_NODE_POINT_DISTRIBUTE_RANDOM: + points = random_scatter_points_from_mesh(mesh_in, density, density_factors, seed); + break; + case GEO_NODE_POINT_DISTRIBUTE_POISSON: + const float min_dist = params.extract_input<float>("Minimum Distance"); + points = poisson_scatter_points_from_mesh(mesh_in, density, min_dist, density_factors, seed); + break; + } PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size()); memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size()); @@ -135,6 +298,7 @@ void register_node_type_geo_point_distribute() geo_node_type_base( &ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out); + node_type_update(&ntype, node_point_distribute_update); ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc new file mode 100644 index 00000000000..47764efa15d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc @@ -0,0 +1,281 @@ +/* + * 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. + */ + +/* + * Based on Cem Yuksel. 2015. Sample Elimination for Generating Poisson Disk Sample + * ! Sets. Computer Graphics Forum 34, 2 (May 2015), 25-32. + * ! http://www.cemyuksel.com/research/sampleelimination/ + * Copyright (c) 2016, Cem Yuksel <cem@cemyuksel.com> + * All rights reserved. + */ + +#include "BLI_inplace_priority_queue.hh" +#include "BLI_kdtree.h" + +#include "node_geometry_util.hh" + +#include <iostream> +#include <string.h> + +namespace blender::nodes { + +static void tile_point(Vector<float3> *tiled_points, + Vector<size_t> *indices, + const float maximum_distance, + const float3 boundbox, + float3 const &point, + size_t index, + int dimension = 0) +{ + for (int dimension_iter = dimension; dimension_iter < 3; dimension_iter++) { + if (boundbox[dimension_iter] - point[dimension_iter] < maximum_distance) { + float3 point_tiled = point; + point_tiled[dimension_iter] -= boundbox[dimension_iter]; + + tiled_points->append(point_tiled); + indices->append(index); + + tile_point(tiled_points, + indices, + maximum_distance, + boundbox, + point_tiled, + index, + dimension_iter + 1); + } + + if (point[dimension_iter] < maximum_distance) { + float3 point_tiled = point; + point_tiled[dimension_iter] += boundbox[dimension_iter]; + + tiled_points->append(point_tiled); + indices->append(index); + + tile_point(tiled_points, + indices, + maximum_distance, + boundbox, + point_tiled, + index, + dimension_iter + 1); + } + } +} + +/** + * Returns the weight the point gets based on the distance to another point. + */ +static float point_weight_influence_get(const float maximum_distance, + const float minimum_distance, + float distance) +{ + const float alpha = 8.0f; + + if (distance < minimum_distance) { + distance = minimum_distance; + } + + return std::pow(1.0f - distance / maximum_distance, alpha); +} + +/** + * Weight each point based on their proximity to its neighbors + * + * For each index in the weight array add a weight based on the proximity the + * corresponding point has with its neighboors. + **/ +static void points_distance_weight_calculate(Vector<float> *weights, + const size_t point_id, + const float3 *input_points, + const void *kd_tree, + const float minimum_distance, + const float maximum_distance, + InplacePriorityQueue<float> *heap) +{ + KDTreeNearest_3d *nearest_point = nullptr; + int neighbors = BLI_kdtree_3d_range_search( + (KDTree_3d *)kd_tree, input_points[point_id], &nearest_point, maximum_distance); + + for (int i = 0; i < neighbors; i++) { + size_t neighbor_point_id = nearest_point[i].index; + + if (neighbor_point_id >= weights->size()) { + continue; + } + + /* The point should not influence itself. */ + if (neighbor_point_id == point_id) { + continue; + } + + const float weight_influence = point_weight_influence_get( + maximum_distance, minimum_distance, nearest_point[i].dist); + + /* In the first pass we just the weights. */ + if (heap == nullptr) { + (*weights)[point_id] += weight_influence; + } + /* When we run again we need to update the weights and the heap. */ + else { + (*weights)[neighbor_point_id] -= weight_influence; + heap->priority_decreased(neighbor_point_id); + } + } + + if (nearest_point) { + MEM_freeN(nearest_point); + } +} + +/** + * Returns the minimum radius fraction used by the default weight function. + */ +static float weight_limit_fraction_get(const size_t input_size, const size_t output_size) +{ + const float beta = 0.65f; + const float gamma = 1.5f; + float ratio = float(output_size) / float(input_size); + return (1.0f - std::pow(ratio, gamma)) * beta; +} + +/** + * Tile the input points. + */ +static void points_tiling(const float3 *input_points, + const size_t input_size, + void **kd_tree, + const float maximum_distance, + const float3 boundbox) + +{ + Vector<float3> tiled_points(input_points, input_points + input_size); + Vector<size_t> indices(input_size); + + for (size_t i = 0; i < input_size; i++) { + indices[i] = i; + } + + /* Tile the tree based on the boundbox. */ + for (size_t i = 0; i < input_size; i++) { + tile_point(&tiled_points, &indices, maximum_distance, boundbox, input_points[i], i); + } + + /* Build a new tree with the new indices and tiled points. */ + *kd_tree = BLI_kdtree_3d_new(tiled_points.size()); + for (size_t i = 0; i < tiled_points.size(); i++) { + BLI_kdtree_3d_insert(*(KDTree_3d **)kd_tree, indices[i], tiled_points[i]); + } + BLI_kdtree_3d_balance(*(KDTree_3d **)kd_tree); +} + +static void weighted_sample_elimination(const float3 *input_points, + const size_t input_size, + float3 *output_points, + const size_t output_size, + const float maximum_distance, + const float3 boundbox, + const bool do_copy_eliminated) +{ + const float minimum_distance = maximum_distance * + weight_limit_fraction_get(input_size, output_size); + + void *kd_tree = nullptr; + points_tiling(input_points, input_size, &kd_tree, maximum_distance, boundbox); + + /* Assign weights to each sample. */ + Vector<float> weights(input_size, 0.0f); + for (size_t point_id = 0; point_id < weights.size(); point_id++) { + points_distance_weight_calculate( + &weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, nullptr); + } + + /* Remove the points based on their weight. */ + InplacePriorityQueue<float> heap(weights); + + size_t sample_size = input_size; + while (sample_size > output_size) { + /* For each sample around it, remove its weight contribution and update the heap. */ + size_t point_id = heap.pop_index(); + points_distance_weight_calculate( + &weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, &heap); + sample_size--; + } + + /* Copy the samples to the output array. */ + size_t target_size = do_copy_eliminated ? input_size : output_size; + for (size_t i = 0; i < target_size; i++) { + size_t index = heap.all_indices()[i]; + output_points[i] = input_points[index]; + } + + /* Cleanup. */ + BLI_kdtree_3d_free((KDTree_3d *)kd_tree); +} + +static void progressive_sampling_reorder(Vector<float3> *output_points, + float maximum_density, + float3 boundbox) +{ + /* Re-order the points for progressive sampling. */ + Vector<float3> temporary_points(output_points->size()); + float3 *source_points = output_points->data(); + float3 *dest_points = temporary_points.data(); + size_t source_size = output_points->size(); + size_t dest_size = 0; + + while (source_size >= 3) { + dest_size = source_size * 0.5f; + + /* Changes the weight function radius using half of the number of samples. + * It is used for progressive sampling. */ + maximum_density *= std::sqrt(2.0f); + weighted_sample_elimination( + source_points, source_size, dest_points, dest_size, maximum_density, boundbox, true); + + if (dest_points != output_points->data()) { + memcpy((*output_points)[dest_size], + dest_points[dest_size], + (source_size - dest_size) * sizeof(float3)); + } + + /* Swap the arrays around. */ + float3 *points_iter = source_points; + source_points = dest_points; + dest_points = points_iter; + source_size = dest_size; + } + if (source_points != output_points->data()) { + memcpy(output_points->data(), source_points, dest_size * sizeof(float3)); + } +} + +void poisson_disk_point_elimination(Vector<float3> const *input_points, + Vector<float3> *output_points, + float maximum_density, + float3 boundbox) +{ + weighted_sample_elimination(input_points->data(), + input_points->size(), + output_points->data(), + output_points->size(), + maximum_density, + boundbox, + false); + + progressive_sampling_reorder(output_points, maximum_density, boundbox); +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 6a22adb25a7..eef2c6c9125 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -96,6 +96,9 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type( if (found_socket->type == SOCK_RGBA) { return CD_PROP_COLOR; } + if (found_socket->type == SOCK_BOOLEAN) { + return CD_PROP_BOOL; + } BLI_assert(false); return default_type; diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc index ec5527a2970..2e4196af156 100644 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ b/source/blender/nodes/intern/node_tree_multi_function.cc @@ -208,6 +208,10 @@ static DataTypeConversions create_implicit_conversions() conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); }); add_implicit_conversion<Color4f, float>( conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); }); + add_implicit_conversion<float3, bool>( + conversions, "float3 to boolean", [](float3 a) { return a.length_squared() == 0.0f; }); + add_implicit_conversion<bool, float3>( + conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); }); return conversions; } diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 46b4dbc96d7..366d801a888 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -26,7 +26,6 @@ struct ID; /* DNA_ID.h */ struct ListBase; /* DNA_listBase.h */ struct Object; /* DNA_object_types.h */ struct PathResolvedRNA; -struct ReportList; struct Text; /* defined in DNA_text_types.h */ struct bConstraint; /* DNA_constraint_types.h */ struct bConstraintOb; /* DNA_constraint_types.h */ diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 4dd2b300700..3e73ac77fc6 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -114,7 +114,7 @@ typedef struct RenderResult { /* target image size */ int rectx, recty; - short crop, sample_nr; + short sample_nr; /* The following rect32, rectf and rectz buffers are for temporary storage only, * for RenderResult structs created in #RE_AcquireResultImage - which do not have RenderView */ diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 769077c0e8c..5685911c42e 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -288,7 +288,7 @@ RenderResult *RE_engine_begin_result( disprect.ymin = y; disprect.ymax = y + h; - result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); + result = render_result_new(re, &disprect, RR_USE_MEM, layername, viewname); /* todo: make this thread safe */ @@ -846,7 +846,7 @@ int RE_engine_render(Render *re, int do_all) if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) { savebuffers = RR_USE_EXR; } - re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 3d19e5e6c15..6b55b82ac97 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -905,7 +905,7 @@ static void render_result_rescale(Render *re) if (src_rectf != NULL) { float *dst_rectf = NULL; - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, ""); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, ""); if (re->result != NULL) { dst_rectf = RE_RenderViewGetById(re->result, 0)->rectf; @@ -1162,7 +1162,7 @@ static void render_result_uncrop(Render *re) /* weak is: it chances disprect from border */ render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + rres = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); render_result_clone_passes(re, rres, NULL); @@ -1358,7 +1358,7 @@ static void do_render_composite(Render *re) if ((re->r.mode & R_CROP) == 0) { render_result_disprect_to_full_resolution(re); } - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index dfce51ec3ab..1ed894751ce 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -285,12 +285,8 @@ RenderPass *render_layer_add_pass(RenderResult *rr, /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new(Render *re, - rcti *partrct, - int crop, - int savebuffers, - const char *layername, - const char *viewname) +RenderResult *render_result_new( + Render *re, rcti *partrct, int savebuffers, const char *layername, const char *viewname) { RenderResult *rr; RenderLayer *rl; @@ -308,9 +304,7 @@ RenderResult *render_result_new(Render *re, rr->rectx = rectx; rr->recty = recty; rr->renrect.xmin = 0; - rr->renrect.xmax = rectx - 2 * crop; - /* crop is one or two extra pixels rendered for filtering, is used for merging and display too */ - rr->crop = crop; + rr->renrect.xmax = rectx; /* tilerect is relative coordinates within render disprect. do not subtract crop yet */ rr->tilerect.xmin = partrct->xmin - re->disprect.xmin; @@ -827,20 +821,8 @@ static void do_merge_tile( copylen = tilex = rrpart->rectx; tiley = rrpart->recty; - if (rrpart->crop) { /* filters add pixel extra */ - tile += pixsize * (rrpart->crop + ((size_t)rrpart->crop) * tilex); - - copylen = tilex - 2 * rrpart->crop; - tiley -= 2 * rrpart->crop; - - ofs = (((size_t)rrpart->tilerect.ymin) + rrpart->crop) * rr->rectx + - (rrpart->tilerect.xmin + rrpart->crop); - target += pixsize * ofs; - } - else { - ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin); - target += pixsize * ofs; - } + ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin); + target += pixsize * ofs; copylen *= sizeof(float) * pixsize; tilex *= pixsize; @@ -1107,7 +1089,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons { RenderLayer *rlp, *rl; RenderPass *rpassp; - int offs, partx, party; + int partx, party; BLI_thread_lock(LOCK_IMAGE); @@ -1120,13 +1102,6 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons continue; } - if (rrpart->crop) { /* filters add pixel extra */ - offs = (rrpart->crop + rrpart->crop * rrpart->rectx); - } - else { - offs = 0; - } - /* passes are allocated in sync */ for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { const int xstride = rpassp->channels; @@ -1141,13 +1116,13 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons fullname, xstride, xstride * rrpart->rectx, - rpassp->rect + a + xstride * offs); + rpassp->rect + a); } } } - party = rrpart->tilerect.ymin + rrpart->crop; - partx = rrpart->tilerect.xmin + rrpart->crop; + party = rrpart->tilerect.ymin; + partx = rrpart->tilerect.xmin; for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { rl = RE_GetRenderLayer(rr, rlp->name); @@ -1267,7 +1242,7 @@ void render_result_exr_file_end(Render *re, RenderEngine *engine) /* Create new render result in memory instead of on disk. */ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); render_result_free_list(&re->fullresult, re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); LISTBASE_FOREACH (RenderLayer *, rl, &re->result->layers) { @@ -1429,7 +1404,7 @@ bool render_result_exr_file_cache_read(Render *re) char *root = U.render_cachedir; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* First try cache. */ render_result_exr_file_cache_path(re->scene, root, str); diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h index 5c487223e94..67edd075e24 100644 --- a/source/blender/render/intern/render_result.h +++ b/source/blender/render/intern/render_result.h @@ -51,7 +51,6 @@ extern "C" { struct RenderResult *render_result_new(struct Render *re, struct rcti *partrct, - int crop, int savebuffers, const char *layername, const char *viewname); diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 11e213d842d..3a9c23de5cc 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -23,10 +23,15 @@ * \ingroup sequencer */ +#include "DNA_scene_types.h" + #ifdef __cplusplus extern "C" { #endif +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct Depsgraph; struct Editing; struct GPUOffScreen; @@ -47,6 +52,7 @@ struct bSound; struct BlendWriter; struct BlendDataReader; struct BlendLibReader; +struct SequencerToolSettings; /* Wipe effect */ enum { @@ -161,7 +167,7 @@ void SEQ_render_new_render_data(struct Main *bmain, int preview_render_size, int for_render, SeqRenderData *r_context); -int SEQ_render_evaluate_frame(struct Scene *scene, int timeline_frame); +int SEQ_render_evaluate_frame(struct ListBase *seqbase, int timeline_frame); struct StripElem *SEQ_render_give_stripelem(struct Sequence *seq, int timeline_frame); /* ********************************************************************** @@ -179,9 +185,16 @@ void SEQ_render_pixel_from_sequencer_space_v4(struct Scene *scene, float pixel[4 * Sequencer scene functions * ********************************************************************** */ +struct SequencerToolSettings *SEQ_tool_settings_init(void); +void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings); +eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene); +void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method); + +struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); struct Editing *BKE_sequencer_editing_get(struct Scene *scene, bool alloc); struct Editing *BKE_sequencer_editing_ensure(struct Scene *scene); void BKE_sequencer_editing_free(struct Scene *scene, const bool do_id_user); +struct ListBase *SEQ_active_seqbase_get(const struct Editing *ed); void BKE_sequencer_sort(struct Scene *scene); struct Sequence *BKE_sequencer_from_elem(ListBase *seqbase, struct StripElem *se); struct Sequence *BKE_sequencer_active_get(struct Scene *scene); @@ -361,6 +374,20 @@ void BKE_sequence_invalidate_cache_in_range(struct Scene *scene, void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int timeline_frame); /* ********************************************************************** + * util.c + * + * Add strips + * ********************************************************************** + */ + +void SEQ_set_scale_to_fit(const struct Sequence *seq, + const int image_width, + const int image_height, + const int preview_width, + const int preview_height, + const eSeqImageFitMethod fit_method); + +/* ********************************************************************** * sequencer.c * * Add strips @@ -376,6 +403,7 @@ typedef struct SeqLoadInfo { int type; int len; /* only for image strips */ char path[1024]; /* 1024 = FILE_MAX */ + eSeqImageFitMethod fit_method; /* multiview */ char views_format; @@ -595,6 +623,33 @@ struct Sequence *SEQ_edit_strip_split(struct Main *bmain, struct Sequence *seq, const int timeline_frame, const eSeqSplitMethod method); +bool SEQ_edit_remove_gaps(struct Scene *scene, + struct ListBase *seqbase, + const int initial_frame, + const bool remove_all_gaps); + +/* ********************************************************************** + * strip_time.c + * + * Editing functions + * ********************************************************************** + */ + +void SEQ_timeline_boundbox(const struct Scene *scene, + const struct ListBase *seqbase, + struct rctf *rect); + +/* ********************************************************************** + * strip_transform.c + * + * Editing functions + * ********************************************************************** + */ + +void SEQ_offset_after_frame(struct Scene *scene, + struct ListBase *seqbase, + const int delta, + const int timeline_frame); #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/effects.h b/source/blender/sequencer/intern/effects.h index 58e0a97d4c5..6a94c0ea9d9 100644 --- a/source/blender/sequencer/intern/effects.h +++ b/source/blender/sequencer/intern/effects.h @@ -28,8 +28,8 @@ extern "C" { #endif struct Scene; -struct Sequence; struct SeqRenderData; +struct Sequence; /* ********************************************************************** * sequencer.c diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h index 0fcf0548628..2cb35670a2c 100644 --- a/source/blender/sequencer/intern/image_cache.h +++ b/source/blender/sequencer/intern/image_cache.h @@ -30,8 +30,8 @@ extern "C" { struct ImBuf; struct Main; struct Scene; -struct Sequence; struct SeqRenderData; +struct Sequence; #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/multiview.h b/source/blender/sequencer/intern/multiview.h index e1f998d18e2..bbc66c6f84c 100644 --- a/source/blender/sequencer/intern/multiview.h +++ b/source/blender/sequencer/intern/multiview.h @@ -27,14 +27,7 @@ extern "C" { #endif -struct Editing; -struct ImBuf; -struct Main; -struct Mask; struct Scene; -struct Sequence; -struct StripColorBalance; -struct StripElem; /* ********************************************************************** * sequencer.c diff --git a/source/blender/sequencer/intern/prefetch.h b/source/blender/sequencer/intern/prefetch.h index aa47a9dbcfc..1633ee297f8 100644 --- a/source/blender/sequencer/intern/prefetch.h +++ b/source/blender/sequencer/intern/prefetch.h @@ -27,11 +27,9 @@ extern "C" { #endif -struct ImBuf; -struct Main; struct Scene; -struct Sequence; struct SeqRenderData; +struct Sequence; #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/proxy.h b/source/blender/sequencer/intern/proxy.h index a362a318a5a..a65fdcd42fe 100644 --- a/source/blender/sequencer/intern/proxy.h +++ b/source/blender/sequencer/intern/proxy.h @@ -27,10 +27,10 @@ extern "C" { #endif -struct anim; struct ImBuf; struct SeqRenderData; struct Sequence; +struct anim; #define PROXY_MAXFILE (2 * FILE_MAXDIR + FILE_MAXFILE) struct ImBuf *seq_proxy_fetch(const struct SeqRenderData *context, diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 008ea1cd3a0..2e757a06751 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -337,16 +337,17 @@ static int evaluate_seq_frame_gen(Sequence **seq_arr, return totseq; } -int SEQ_render_evaluate_frame(Scene *scene, int timeline_frame) +/** + * Count number of strips in timeline at timeline_frame + * + * \param seqbase: ListBase in which strips are located + * \param timeline_frame: frame on timeline from where gaps are searched for + * \return number of strips + */ +int SEQ_render_evaluate_frame(ListBase *seqbase, int timeline_frame) { - Editing *ed = BKE_sequencer_editing_get(scene, false); Sequence *seq_arr[MAXSEQ + 1]; - - if (ed == NULL) { - return 0; - } - - return evaluate_seq_frame_gen(seq_arr, ed->seqbasep, timeline_frame, 0); + return evaluate_seq_frame_gen(seq_arr, seqbase, timeline_frame, 0); } static bool video_seq_is_rendered(Sequence *seq) @@ -527,7 +528,6 @@ static void sequencer_image_transform_init(void *handle_v, handle->ibuf_source = init_data->ibuf_source; handle->ibuf_out = init_data->ibuf_out; handle->transform = init_data->transform; - handle->scale_to_fit = init_data->scale_to_fit; handle->image_scale_factor = init_data->image_scale_factor; handle->for_render = init_data->for_render; @@ -539,8 +539,8 @@ static void *sequencer_image_transform_do_thread(void *data_v) { const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v; const StripTransform *transform = data->transform; - const float scale_x = transform->scale_x * data->scale_to_fit; - const float scale_y = transform->scale_y * data->scale_to_fit; + const float scale_x = transform->scale_x * data->image_scale_factor; + const float scale_y = transform->scale_y * data->image_scale_factor; const float scale_to_fit_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2; const float scale_to_fit_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2; const float translate_x = transform->xofs * data->image_scale_factor + scale_to_fit_offs_x; @@ -625,10 +625,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context, IMB_filtery(preprocessed_ibuf); } - /* Calculate scale factor, so image fits in preview area with original aspect ratio. */ - const float scale_to_fit_factor = MIN2((float)context->rectx / (float)ibuf->x, - (float)context->recty / (float)ibuf->y); - /* Get scale factor if preview resolution doesn't match project resolution. */ float preview_scale_factor; if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) { @@ -647,10 +643,10 @@ static ImBuf *input_preprocess(const SeqRenderData *context, const int height = ibuf->y; const StripCrop *c = seq->strip->crop; - const int left = c->left / scale_to_fit_factor * preview_scale_factor; - const int right = c->right / scale_to_fit_factor * preview_scale_factor; - const int top = c->top / scale_to_fit_factor * preview_scale_factor; - const int bottom = c->bottom / scale_to_fit_factor * preview_scale_factor; + const int left = c->left; + const int right = c->right; + const int top = c->top; + const int bottom = c->bottom; const float col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; /* Left. */ @@ -672,7 +668,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context, init_data.ibuf_source = ibuf; init_data.ibuf_out = preprocessed_ibuf; init_data.transform = seq->strip->transform; - init_data.scale_to_fit = scale_to_fit_factor; init_data.image_scale_factor = preview_scale_factor; init_data.for_render = context->for_render; IMB_processor_apply_threaded(context->recty, diff --git a/source/blender/sequencer/intern/render.h b/source/blender/sequencer/intern/render.h index d5affeb547b..46748415187 100644 --- a/source/blender/sequencer/intern/render.h +++ b/source/blender/sequencer/intern/render.h @@ -27,7 +27,6 @@ extern "C" { #endif -struct Editing; struct ImBuf; struct ListBase; struct Scene; diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index c998886626c..87b608ef141 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -301,6 +301,46 @@ static void seq_new_fix_links_recursive(Sequence *seq) } } } + +SequencerToolSettings *SEQ_tool_settings_init(void) +{ + SequencerToolSettings *tool_settings = MEM_callocN(sizeof(SequencerToolSettings), + "Sequencer tool settings"); + tool_settings->fit_method = SEQ_SCALE_TO_FIT; + return tool_settings; +} + +void SEQ_tool_settings_free(SequencerToolSettings *tool_settings) +{ + MEM_freeN(tool_settings); +} + +eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + return tool_settings->fit_method; +} + +void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method) +{ + SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + tool_settings->fit_method = fit_method; +} + +/** + * Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase + * + * \param ed: sequence editor data + * \return pointer to active seqbase. returns NULL if ed is NULL + */ +ListBase *SEQ_active_seqbase_get(const Editing *ed) +{ + if (ed == NULL) { + return NULL; + } + + return ed->seqbasep; +} /** \} */ /* -------------------------------------------------------------------- */ @@ -609,4 +649,11 @@ static void seq_free_animdata(Scene *scene, Sequence *seq) } #undef SEQ_RNAPATH_MAXSTR + +SequencerToolSettings *SEQ_tool_settings_copy(SequencerToolSettings *tool_settings) +{ + SequencerToolSettings *tool_settings_copy = MEM_dupallocN(tool_settings); + return tool_settings_copy; +} + /** \} */ diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index d2e4025bdfc..e56dcf888a7 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -118,6 +118,16 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad seq->flag |= seq_load->flag & SEQ_USE_VIEWS; seq_load_apply(CTX_data_main(C), scene, seq, seq_load); + + char file_path[FILE_MAX]; + BLI_join_dirfile(file_path, sizeof(file_path), seq_load->path, seq_load->name); + BLI_path_abs(file_path, BKE_main_blendfile_path(CTX_data_main(C))); + ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name); + if (ibuf != NULL) { + SEQ_set_scale_to_fit(seq, ibuf->x, ibuf->y, scene->r.xsch, scene->r.ysch, seq_load->fit_method); + IMB_freeImBuf(ibuf); + } + BKE_sequence_invalidate_cache_composite(scene, seq); return seq; @@ -275,6 +285,11 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad IMB_anim_load_metadata(anim_arr[0]); seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); + + const float width = IMB_anim_get_image_width(anim_arr[0]); + const float height = IMB_anim_get_image_height(anim_arr[0]); + SEQ_set_scale_to_fit(seq, width, height, scene->r.xsch, scene->r.ysch, seq_load->fit_method); + BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2); BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 3137a471470..a29810cc9ee 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -38,6 +38,8 @@ #include "BKE_scene.h" #include "BKE_sound.h" +#include "strip_time.h" + #include "SEQ_sequencer.h" int BKE_sequence_swap(Sequence *seq_a, Sequence *seq_b, const char **error_str) @@ -319,3 +321,36 @@ Sequence *SEQ_edit_strip_split(Main *bmain, BKE_sequence_calc(scene, right_seq); return right_seq; } + +/** + * Find gap after initial_frame and move strips on right side to close the gap + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param initial_frame: frame on timeline from where gaps are searched for + * \param remove_all_gaps: remove all gaps instead of one gap + * \return true if gap is removed, otherwise false + */ +bool SEQ_edit_remove_gaps(Scene *scene, + ListBase *seqbase, + const int initial_frame, + const bool remove_all_gaps) +{ + GapInfo gap_info = {0}; + seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info); + + if (!gap_info.gap_exists) { + return false; + } + + if (remove_all_gaps) { + while (gap_info.gap_exists) { + SEQ_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame); + seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info); + } + } + else { + SEQ_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame); + } + return true; +} diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index a0ae6d6f16d..d9074b2a683 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -351,3 +351,85 @@ float BKE_sequence_get_fps(Scene *scene, Sequence *seq) } return 0.0f; } + +/** + * Define boundary rectangle of sequencer timeline and fill in rect data + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param rect: data structure describing rectangle, that will be filled in by this function + */ +void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +{ + rect->xmin = scene->r.sfra; + rect->xmax = scene->r.efra + 1; + rect->ymin = 0.0f; + rect->ymax = 8.0f; + + if (seqbase == NULL) { + return; + } + + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (rect->xmin > seq->startdisp - 1) { + rect->xmin = seq->startdisp - 1; + } + if (rect->xmax < seq->enddisp + 1) { + rect->xmax = seq->enddisp + 1; + } + if (rect->ymax < seq->machine + 2) { + rect->ymax = seq->machine + 2; + } + } +} + +/** + * Find first gap between strips after initial_frame and describe it by filling data of r_gap_info + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param initial_frame: frame on timeline from where gaps are searched for + * \param r_gap_info: data structure describing gap, that will be filled in by this function + */ +void seq_time_gap_info_get(const Scene *scene, + ListBase *seqbase, + const int initial_frame, + GapInfo *r_gap_info) +{ + rctf rectf; + /* Get first and last frame. */ + SEQ_timeline_boundbox(scene, seqbase, &rectf); + const int sfra = (int)rectf.xmin; + const int efra = (int)rectf.xmax; + int timeline_frame = initial_frame; + r_gap_info->gap_exists = false; + + if (SEQ_render_evaluate_frame(seqbase, initial_frame) == 0) { + /* Search backward for gap_start_frame. */ + for (; timeline_frame >= sfra; timeline_frame--) { + if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) { + break; + } + } + r_gap_info->gap_start_frame = timeline_frame + 1; + timeline_frame = initial_frame; + } + else { + /* Search forward for gap_start_frame. */ + for (; timeline_frame <= efra; timeline_frame++) { + if (SEQ_render_evaluate_frame(seqbase, timeline_frame) == 0) { + r_gap_info->gap_start_frame = timeline_frame; + break; + } + } + } + /* Search forward for gap_end_frame. */ + for (; timeline_frame <= efra; timeline_frame++) { + if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) { + const int gap_end_frame = timeline_frame; + r_gap_info->gap_length = gap_end_frame - r_gap_info->gap_start_frame; + r_gap_info->gap_exists = true; + break; + } + } +} diff --git a/source/blender/sequencer/intern/strip_time.h b/source/blender/sequencer/intern/strip_time.h index e4fb7f1d2ec..ca9a935bc96 100644 --- a/source/blender/sequencer/intern/strip_time.h +++ b/source/blender/sequencer/intern/strip_time.h @@ -27,12 +27,24 @@ extern "C" { #endif +struct ListBase; struct Scene; struct Sequence; float seq_give_frame_index(struct Sequence *seq, float timeline_frame); void seq_update_sound_bounds_recursive(struct Scene *scene, struct Sequence *metaseq); +/* Describes gap between strips in timeline. */ +typedef struct GapInfo { + int gap_start_frame; /* Start frame of the gap. */ + int gap_length; /* Length of the gap. */ + bool gap_exists; /* False if there are no gaps. */ +} GapInfo; +void seq_time_gap_info_get(const struct Scene *scene, + struct ListBase *seqbase, + const int initial_frame, + struct GapInfo *r_gap_info); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 233f8e5b22e..4aabe87bce1 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -397,3 +397,33 @@ bool BKE_sequence_base_shuffle_time(ListBase *seqbasep, return offset ? false : true; } + +/** + * Move strips and markers (if not locked) that start after timeline_frame by delta frames + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param delta: offset in frames to be applied + * \param timeline_frame: frame on timeline from where strips are moved + */ +void SEQ_offset_after_frame(Scene *scene, + ListBase *seqbase, + const int delta, + const int timeline_frame) +{ + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (seq->startdisp >= timeline_frame) { + BKE_sequence_translate(scene, seq, delta); + BKE_sequence_calc(scene, seq); + BKE_sequence_invalidate_cache_preprocessed(scene, seq); + } + } + + if (!scene->toolsettings->lock_markers) { + LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { + if (marker->frame >= timeline_frame) { + marker->frame += delta; + } + } + } +} diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 2b1d36a7709..ab0b65dba7f 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -36,6 +36,7 @@ #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_utildefines.h" #include "BKE_image.h" #include "BKE_main.h" @@ -547,3 +548,36 @@ bool sequencer_seq_generates_image(Sequence *seq) } return false; } + +void SEQ_set_scale_to_fit(const Sequence *seq, + const int image_width, + const int image_height, + const int preview_width, + const int preview_height, + const eSeqImageFitMethod fit_method) +{ + StripTransform *transform = seq->strip->transform; + + switch (fit_method) { + case SEQ_SCALE_TO_FIT: + transform->scale_x = transform->scale_y = MIN2((float)preview_width / (float)image_width, + (float)preview_height / (float)image_height); + + break; + case SEQ_SCALE_TO_FILL: + + transform->scale_x = transform->scale_y = MAX2((float)preview_width / (float)image_width, + (float)preview_height / (float)image_height); + break; + case SEQ_STRETCH_TO_FILL: + transform->scale_x = (float)preview_width / (float)image_width; + transform->scale_y = (float)preview_height / (float)image_height; + break; + case SEQ_USE_ORIGINAL_SIZE: + transform->scale_x = 1.0f; + transform->scale_y = 1.0f; + break; + } + + return; +} diff --git a/source/blender/sequencer/intern/utils.h b/source/blender/sequencer/intern/utils.h index fe6041ec5e8..f30ea753d37 100644 --- a/source/blender/sequencer/intern/utils.h +++ b/source/blender/sequencer/intern/utils.h @@ -28,7 +28,6 @@ extern "C" { #endif struct Scene; -struct anim; bool sequencer_seq_generates_image(struct Sequence *seq); void seq_open_anim_file(struct Scene *scene, struct Sequence *seq, bool openfile); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index ac27862d507..c1ae307eb55 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1204,7 +1204,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, RNA_STRUCT_END; } else { - LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &op->opm->type->macro) { + LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) { wmOperatorType *otm = WM_operatortype_find(macro->idname, 0); wmOperator *opm = wm_operator_create(wm, otm, macro->ptr, NULL); @@ -2293,6 +2293,11 @@ static int wm_handler_fileselect_do(bContext *C, } wm_handler_op_context(C, handler, ctx_win->eventstate); + ScrArea *handler_area = CTX_wm_area(C); + /* Make sure new context area is ready, the operator callback may operate on it. */ + if (handler_area) { + ED_area_do_refresh(C, handler_area); + } /* Needed for #UI_popup_menu_reports. */ diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index baa47098bd3..c26acfc9802 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -23,7 +23,6 @@ #pragma once -struct ARegion; struct ReportList; struct wmWindow; |