Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
92bc9477d1
|
@ -1 +1 @@
|
|||
Subproject commit 9e40c01dffd3f720b23b906d20df8e999d34a4af
|
||||
Subproject commit 877a343fed9613d8e02e7fe7181d3bbb628506f2
|
|
@ -1 +1 @@
|
|||
Subproject commit 1a3f127714e8da9f0af12d9a174dae9793ae63c1
|
||||
Subproject commit a3fa40ec0ba525bc96cbfad49f854a0230b0524e
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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"""
|
||||
|
||||
|
|
|
@ -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"""
|
||||
|
||||
|
|
|
@ -573,6 +573,7 @@ class QuickLiquid(Operator):
|
|||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
QuickExplode,
|
||||
QuickFur,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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="")
|
||||
|
|
|
@ -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 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,
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -139,12 +139,11 @@ class SEQUENCER_HT_header(Header):
|
|||
|
||||
SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
|
||||
|
||||
layout.separator_spacer()
|
||||
|
||||
if st.view_type in {'PREVIEW', 'SEQUENCER_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 +156,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 +181,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 +373,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 +403,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()
|
||||
|
||||
|
@ -2222,6 +2293,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,
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -2938,10 +2938,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,
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -481,7 +481,7 @@ 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"),
|
||||
|
|
|
@ -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.
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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()
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -45,8 +45,6 @@ struct NlaKeyframingContext;
|
|||
struct PathResolvedRNA;
|
||||
struct PointerRNA;
|
||||
struct PropertyRNA;
|
||||
struct ReportList;
|
||||
struct Scene;
|
||||
struct bAction;
|
||||
struct bActionGroup;
|
||||
struct bContext;
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct BlendWriter;
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
struct ID;
|
||||
struct PreviewImage;
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ extern "C" {
|
|||
struct CustomData;
|
||||
struct CustomDataLayer;
|
||||
struct ID;
|
||||
struct PointerRNA;
|
||||
struct ReportList;
|
||||
|
||||
/* Attribute.domain */
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
#include "BLI_color.hh"
|
||||
#include "BLI_float3.hh"
|
||||
|
||||
struct Mesh;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
using fn::CPPType;
|
||||
|
|
|
@ -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 7
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct BVHTree;
|
||||
struct Collection;
|
||||
struct CollisionModifierData;
|
||||
struct BVHTree;
|
||||
struct Depsgraph;
|
||||
struct MVert;
|
||||
struct MVertTri;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -35,9 +35,7 @@ struct BMLoop;
|
|||
struct BMesh;
|
||||
struct BoundBox;
|
||||
struct Depsgraph;
|
||||
struct EditMeshData;
|
||||
struct Mesh;
|
||||
struct MeshStatVis;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@
|
|||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_geometry_set.h"
|
||||
|
||||
struct Mesh;
|
||||
struct PointCloud;
|
||||
struct Object;
|
||||
struct Collection;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Ipo;
|
||||
struct Main;
|
||||
|
||||
void do_versions_ipos_to_animato(struct Main *main);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
struct GHash;
|
||||
struct ID;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "BLI_utildefines.h"
|
||||
|
||||
struct BLI_Stack;
|
||||
struct BMEditMesh;
|
||||
struct BMesh;
|
||||
struct BMeshCreateParams;
|
||||
struct BMeshFromMeshParams;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -1347,7 +1347,7 @@ 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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -48,7 +48,6 @@ struct PBVH;
|
|||
struct PBVHNode;
|
||||
struct SubdivCCG;
|
||||
struct TaskParallelSettings;
|
||||
struct TaskParallelTLS;
|
||||
|
||||
typedef struct PBVH PBVH;
|
||||
typedef struct PBVHNode PBVHNode;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -51,7 +51,6 @@ struct View3D;
|
|||
struct View3DShading;
|
||||
struct WorkSpace;
|
||||
struct bContext;
|
||||
struct bContextDataResult;
|
||||
struct bScreen;
|
||||
struct uiLayout;
|
||||
struct uiList;
|
||||
|
|
|
@ -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)) || \
|
||||
|
|
|
@ -26,7 +26,6 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
struct Main;
|
||||
struct Speaker;
|
||||
|
||||
void *BKE_speaker_add(struct Main *bmain, const char *name);
|
||||
|
||||
|
|
|
@ -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
|
||||
python_version,
|
||||
basename,
|
||||
#if defined(WIN32) && !defined(NDEBUG)
|
||||
basename_debug,
|
||||
#endif
|
||||
python_version,
|
||||
basename,
|
||||
};
|
||||
bool is_found = false;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -725,7 +735,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 +845,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 +892,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) {
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -4737,7 +4737,7 @@ 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();
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -427,6 +427,25 @@ template<typename From, typename To>
|
|||
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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ extern "C" {
|
|||
#define BLENDER_MAX_THREADS 1024
|
||||
|
||||
struct ListBase;
|
||||
struct TaskScheduler;
|
||||
|
||||
/* Threading API */
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1234,18 +1234,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 +1290,39 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
|
||||
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
if (STREQ(node->idname, "GeometryNodeRandomAttribute")) {
|
||||
STRNCPY(node->idname, "GeometryNodeAttributeRandomize");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -39,7 +39,6 @@ struct Main;
|
|||
struct Object;
|
||||
struct Scene;
|
||||
struct Simulation;
|
||||
struct ViewLayer;
|
||||
struct bNodeTree;
|
||||
|
||||
#include "BLI_sys_types.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;
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
struct Main;
|
||||
|
||||
namespace blender {
|
||||
namespace deg {
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -25,11 +25,9 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
struct GPUBatch;
|
||||
struct GPUTexture;
|
||||
struct ImBuf;
|
||||
struct Image;
|
||||
struct rcti;
|
||||
|
||||
/* *********** LISTS *********** */
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@
|
|||
#include "DEG_depsgraph.h"
|
||||
|
||||
struct GPUBatch;
|
||||
struct GPUFrameBuffer;
|
||||
struct GPUMaterial;
|
||||
struct GPUShader;
|
||||
struct GPUTexture;
|
||||
|
|
|
@ -22,10 +22,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
struct DRWPass;
|
||||
struct DRWShadingGroup;
|
||||
struct FluidModifierData;
|
||||
struct GPUMaterial;
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
struct ParticleSystem;
|
||||
|
|
|
@ -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! */
|
||||
|
|
|
@ -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,27 +1054,16 @@ 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 */
|
||||
|
|
|
@ -31,7 +31,6 @@ struct Base;
|
|||
struct Bone;
|
||||
struct Depsgraph;
|
||||
struct EditBone;
|
||||
struct IDProperty;
|
||||
struct ListBase;
|
||||
struct Main;
|
||||
struct Mesh;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ struct ScrArea;
|
|||
struct SnapObjectContext;
|
||||
struct ToolSettings;
|
||||
struct View3D;
|
||||
struct ViewLayer;
|
||||
struct bContext;
|
||||
|
||||
struct Material;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -53,7 +53,6 @@ struct uiLayout;
|
|||
struct wmKeyConfig;
|
||||
struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
struct wmWindowManager;
|
||||
|
||||
/* object_edit.c */
|
||||
/* context.object */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -31,7 +31,6 @@ struct BMVert;
|
|||
struct ARegion;
|
||||
struct Depsgraph;
|
||||
struct ListBase;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct View3D;
|
||||
|
|
|
@ -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 ************* */
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
struct ARegion;
|
||||
struct Main;
|
||||
struct bContext;
|
||||
struct wmEvent;
|
||||
struct wmOperator;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue