WIP: Vulkan: Clearing Storage Buffers #105299

Closed
Jeroen Bakker wants to merge 73 commits from Jeroen-Bakker:gpu-storage-buffer-clear into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
40 changed files with 656 additions and 143 deletions
Showing only changes of commit 64f81d7c29 - Show all commits

View File

@ -17,11 +17,13 @@ ExternalProject_Add(external_xvidcore
INSTALL_DIR ${LIBDIR}/xvidcore
)
ExternalProject_Add_Step(external_xvidcore after_install
COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true
COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a
DEPENDEES install
)
if(WIN32)
ExternalProject_Add_Step(external_xvidcore after_install
COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true
COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a
DEPENDEES install
)
endif()
if(MSVC)
set_target_properties(external_xvidcore PROPERTIES FOLDER Mingw)

View File

@ -142,7 +142,7 @@ def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tupl
make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
if make_exe is None:
print("Make command not found in: %r not found" % project_path)
print("Make command not found: CMAKE_MAKE_PROGRAM")
return None, None
make_exe_basename = os.path.basename(make_exe)

View File

@ -42,6 +42,7 @@ def parse_arguments() -> argparse.Namespace:
parser.add_argument("--svn-branch", default=None)
parser.add_argument("--git-command", default="git")
parser.add_argument("--use-linux-libraries", action="store_true")
parser.add_argument("--architecture", type=str, choices=("x86_64", "amd64", "arm64",))
return parser.parse_args()
@ -51,6 +52,19 @@ def get_blender_git_root() -> str:
# Setup for precompiled libraries and tests from svn.
def get_effective_architecture(args: argparse.Namespace) -> str:
architecture = args.architecture
if architecture:
assert isinstance(architecture, str)
return architecture
# Check platform.version to detect arm64 with x86_64 python binary.
if "ARM64" in platform.version():
return "arm64"
return platform.machine().lower()
def svn_update(args: argparse.Namespace, release_version: Optional[str]) -> None:
svn_non_interactive = [args.svn_command, '--non-interactive']
@ -58,11 +72,11 @@ def svn_update(args: argparse.Namespace, release_version: Optional[str]) -> None
svn_url = make_utils.svn_libraries_base_url(release_version, args.svn_branch)
# Checkout precompiled libraries
architecture = get_effective_architecture(args)
if sys.platform == 'darwin':
# Check platform.version to detect arm64 with x86_64 python binary.
if platform.machine() == 'arm64' or ('ARM64' in platform.version()):
if architecture == 'arm64':
lib_platform = "darwin_arm64"
elif platform.machine() == 'x86_64':
elif architecture == 'x86_64':
lib_platform = "darwin"
else:
lib_platform = None
@ -256,14 +270,15 @@ if __name__ == "__main__":
blender_skip_msg = ""
submodules_skip_msg = ""
# Test if we are building a specific release version.
branch = make_utils.git_branch(args.git_command)
if branch == 'HEAD':
sys.stderr.write('Blender git repository is in detached HEAD state, must be in a branch\n')
sys.exit(1)
tag = make_utils.git_tag(args.git_command)
release_version = make_utils.git_branch_release_version(branch, tag)
blender_version = make_utils. parse_blender_version()
if blender_version.cycle != 'alpha':
major = blender_version.version // 100
minor = blender_version.version % 100
branch = f"blender-v{major}.{minor}-release"
release_version: Optional[str] = f"{major}.{minor}"
else:
branch = 'main'
release_version = None
if not args.no_libraries:
svn_update(args, release_version)

View File

@ -122,7 +122,7 @@ KERNEL_STRUCT_MEMBER(guiding, bool, use_surface_guiding, KERNEL_FEATURE_PATH_GUI
KERNEL_STRUCT_MEMBER(guiding, float, sample_surface_guiding_rand, KERNEL_FEATURE_PATH_GUIDING)
/* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob)*/
KERNEL_STRUCT_MEMBER(guiding, float, surface_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
/* Probability of sampling a BSSRDF closure instead of a BSDF closure*/
/* Probability of sampling a BSSRDF closure instead of a BSDF closure. */
KERNEL_STRUCT_MEMBER(guiding, float, bssrdf_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
/* If volume guiding is enabled */
KERNEL_STRUCT_MEMBER(guiding, bool, use_volume_guiding, KERNEL_FEATURE_PATH_GUIDING)

View File

@ -1177,7 +1177,7 @@ void LightManager::device_update(Device *device,
void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_background)
{
/* to-do: check if the light tree member variables need to be wrapped in a conditional too*/
/* TODO: check if the light tree member variables need to be wrapped in a conditional too. */
dscene->light_tree_nodes.free();
dscene->light_tree_emitters.free();
dscene->light_to_tree.free();

View File

@ -310,6 +310,9 @@ void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyl
monitor.cbSize = sizeof(MONITORINFOEX);
monitor.dwFlags = 0;
/* We'll need this value before it is altered for checking later. */
LONG requested_top = win_rect->top;
/* Note that with MonitorFromPoint using MONITOR_DEFAULTTONEAREST, it will return
* the exact monitor if there is one at the location or the nearest monitor if not. */
@ -355,6 +358,9 @@ void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyl
/* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be
* correctly outside of monitor bounds. NOTE: You cannot specify #WS_OVERLAPPED when calling. */
if (fpAdjustWindowRectExForDpi) {
/* Use the DPI of the monitor that is at the middle of the rect. */
hmonitor = MonitorFromRect(win_rect, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
UINT dpiX, dpiY;
GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
fpAdjustWindowRectExForDpi(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle, dpiX);
@ -362,6 +368,14 @@ void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyl
else {
AdjustWindowRectEx(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle);
}
/* Don't hide the title bar. Check the working area of the monitor at the top-left corner, using
* the original top since the justWindowRects might have altered it to different monitor. */
pt.x = win_rect->left;
pt.y = requested_top;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->top = max(monitor.rcWork.top, win_rect->top);
}
bool GHOST_WindowWin32::getValid() const

View File

@ -3513,6 +3513,9 @@ def km_animation_channels(params):
("anim.channels_ungroup", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
# Menus.
*_template_items_context_menu("DOPESHEET_MT_channel_context_menu", params.context_menu_event),
# View
("anim.channel_view_pick", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
("anim.channels_view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
])
return keymap

View File

@ -25,7 +25,7 @@ def build_default_empty_geometry_node_group(name):
def geometry_node_group_empty_new():
group = build_default_empty_geometry_node_group(data_("Geometry Nodes"))
group.links.new(group.nodes["Group Input"].outputs[0], group.nodes["Group Output"].inputs[0])
group.links.new(group.nodes[data_("Group Input")].outputs[0], group.nodes[data_("Group Output")].inputs[0])
return group
@ -121,8 +121,8 @@ class MoveModifierToNodes(Operator):
group_node.node_tree = old_group
group_node.update()
group_input_node = group.nodes["Group Input"]
group_output_node = group.nodes["Group Output"]
group_input_node = group.nodes[data_("Group Input")]
group_output_node = group.nodes[data_("Group Output")]
# Copy default values for inputs and create named attribute input nodes.
input_nodes = []
@ -173,6 +173,7 @@ class MoveModifierToNodes(Operator):
first_geometry_output = group_node_output
# Adjust locations of store named attribute nodes and move group output.
# Note that the node group has its sockets names translated, while the built-in nodes don't.
if store_nodes:
for i, node in enumerate(store_nodes):
node.location.x = (i + 1) * 175
@ -182,9 +183,10 @@ class MoveModifierToNodes(Operator):
group.links.new(first_geometry_output, store_nodes[0].inputs["Geometry"])
for i in range(len(store_nodes) - 1):
group.links.new(store_nodes[i].outputs["Geometry"], store_nodes[i + 1].inputs["Geometry"])
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs["Geometry"])
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs[data_("Geometry")])
else:
group.links.new(first_geometry_output, group_output_node.inputs["Geometry"])
group.links.new(first_geometry_output, group_output_node.inputs[data_("Geometry")])
modifier.node_group = group

View File

@ -556,7 +556,7 @@ class WM_OT_context_toggle_enum(Operator):
class WM_OT_context_cycle_int(Operator):
"""Set a context value (useful for cycling active material, """ \
"""vertex keys, groups, etc.)"""
"""shape keys, groups, etc.)"""
bl_idname = "wm.context_cycle_int"
bl_label = "Context Int Cycle"
bl_options = {'UNDO', 'INTERNAL'}

View File

@ -78,7 +78,7 @@ class PARTICLE_MT_context_menu(Menu):
props.use_active = False
props.remove_target_particles = True
if psys.settings.type == 'HAIR':
if psys is not None and psys.settings.type == 'HAIR':
layout.operator(
"curves.convert_from_particle_system",
text="Convert to Curves")

View File

@ -474,6 +474,9 @@ class DOPESHEET_MT_channel(Menu):
layout.separator()
layout.operator("anim.channels_fcurves_enable")
layout.separator()
layout.operator("anim.channels_view_selected")
class DOPESHEET_MT_key(Menu):
bl_label = "Key"
@ -601,6 +604,9 @@ class DOPESHEET_MT_gpencil_channel(Menu):
layout.separator()
layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
layout.separator()
layout.operator("anim.channels_view_selected")
class DOPESHEET_MT_gpencil_key(Menu):
bl_label = "Key"
@ -689,6 +695,9 @@ class DOPESHEET_MT_channel_context_menu(Menu):
# This menu is used from the graph editor too.
is_graph_editor = context.area.type == 'GRAPH_EDITOR'
layout.separator()
layout.operator("anim.channels_view_selected")
layout.operator("anim.channels_setting_enable", text="Mute Channels").type = 'MUTE'
layout.operator("anim.channels_setting_disable", text="Unmute Channels").type = 'MUTE'
layout.separator()

View File

@ -239,6 +239,9 @@ class GRAPH_MT_channel(Menu):
layout.separator()
layout.operator("anim.channels_fcurves_enable")
layout.separator()
layout.operator("anim.channels_view_selected")
class GRAPH_MT_key(Menu):
bl_label = "Key"

View File

@ -574,6 +574,11 @@ class NODE_MT_context_menu(Menu):
layout.menu("NODE_MT_context_menu_select_menu")
layout.menu("NODE_MT_context_menu_show_hide_menu")
if active_node:
layout.separator()
props = layout.operator("wm.doc_view_manual", text="Online Manual", icon='URL')
props.doc_id = active_node.bl_idname
class NODE_PT_active_node_generic(Panel):
bl_space_type = 'NODE_EDITOR'

View File

@ -23,8 +23,6 @@ extern "C" {
/** Blender release cycle stage: alpha/beta/rc/release. */
#define BLENDER_VERSION_CYCLE alpha
/* TODO proper version bump. */
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 0

View File

@ -373,6 +373,8 @@ bool BKE_fcurve_calc_range(
/**
* Calculate the extents of F-Curve's data.
* \param range Only calculate the bounds of the FCurve in the given range.
* Does the full range if NULL.
*/
bool BKE_fcurve_calc_bounds(const struct FCurve *fcu,
float *xmin,
@ -380,7 +382,8 @@ bool BKE_fcurve_calc_bounds(const struct FCurve *fcu,
float *ymin,
float *ymax,
bool do_sel_only,
bool include_handles);
bool include_handles,
const float range[2]);
/**
* Return an array of keyed frames, rounded to `interval`.

View File

@ -557,10 +557,11 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[],
/* ...................................... */
/* Helper for calc_fcurve_* functions -> find first and last BezTriple to be used. */
static short get_fcurve_end_keyframes(const FCurve *fcu,
BezTriple **first,
BezTriple **last,
const bool do_sel_only)
static bool get_fcurve_end_keyframes(const FCurve *fcu,
BezTriple **first,
BezTriple **last,
const bool do_sel_only,
const float range[2])
{
bool found = false;
@ -573,11 +574,31 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
return found;
}
int first_index = 0;
int last_index = fcu->totvert - 1;
if (range != NULL) {
/* If a range is passed in find the first and last keyframe within that range. */
bool replace = false;
first_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[0], fcu->totvert, &replace);
last_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[1], fcu->totvert, &replace);
/* If first and last index are the same, no keyframes were found in the range. */
if (first_index == last_index) {
return found;
}
/* The binary search returns an index where a keyframe would be inserted,
so it needs to be clamped to ensure it is in range of the array. */
first_index = clamp_i(first_index, 0, fcu->totvert - 1);
last_index = clamp_i(last_index - 1, 0, fcu->totvert - 1);
}
/* Only include selected items? */
if (do_sel_only) {
/* Find first selected. */
BezTriple *bezt = fcu->bezt;
for (int i = 0; i < fcu->totvert; bezt++, i++) {
for (int i = first_index; i <= last_index; i++) {
BezTriple *bezt = &fcu->bezt[i];
if (BEZT_ISSEL_ANY(bezt)) {
*first = bezt;
found = true;
@ -586,8 +607,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
}
/* Find last selected. */
bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
for (int i = 0; i < fcu->totvert; bezt--, i++) {
for (int i = last_index; i >= first_index; i--) {
BezTriple *bezt = &fcu->bezt[i];
if (BEZT_ISSEL_ANY(bezt)) {
*last = bezt;
found = true;
@ -596,9 +617,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
}
}
else {
/* Use the whole array. */
*first = fcu->bezt;
*last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
*first = &fcu->bezt[first_index];
*last = &fcu->bezt[last_index];
found = true;
}
@ -611,23 +631,25 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
float *ymin,
float *ymax,
const bool do_sel_only,
const bool include_handles)
const bool include_handles,
const float range[2])
{
float xminv = 999999999.0f, xmaxv = -999999999.0f;
float yminv = 999999999.0f, ymaxv = -999999999.0f;
bool foundvert = false;
const bool use_range = range != NULL;
if (fcu->totvert) {
if (fcu->bezt) {
BezTriple *bezt_first = NULL, *bezt_last = NULL;
if (xmin || xmax) {
/* Get endpoint keyframes. */
foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only);
BezTriple *bezt_first = NULL, *bezt_last = NULL;
foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, range);
if (bezt_first) {
BLI_assert(bezt_last != NULL);
foundvert = true;
if (include_handles) {
xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]);
xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]);
@ -645,6 +667,9 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
int i;
for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) {
if (use_range && (bezt->vec[1][0] < range[0] || bezt->vec[1][0] > range[1])) {
continue;
}
if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) {
/* Keyframe itself. */
yminv = min_ff(yminv, bezt->vec[1][1]);
@ -717,10 +742,10 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
}
if (xmin) {
*xmin = 0.0f;
*xmin = use_range ? range[0] : 0.0f;
}
if (xmax) {
*xmax = 1.0f;
*xmax = use_range ? range[1] : 1.0f;
}
if (ymin) {
@ -745,7 +770,7 @@ bool BKE_fcurve_calc_range(
BezTriple *bezt_first = NULL, *bezt_last = NULL;
/* Get endpoint keyframes. */
get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only);
get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, NULL);
if (bezt_first) {
BLI_assert(bezt_last != NULL);

View File

@ -1558,7 +1558,6 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
{
using namespace blender;
using namespace blender::bke;
const AttributeAccessor attributes = mesh->attributes();
Vector<CustomDataLayer, 16> new_layer_to_write;
/* Don't write the boolean UV map sublayers which will be written in the legacy #MLoopUV type. */
@ -1591,24 +1590,30 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
mloopuv_layer.data = mloopuv.data();
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const VArray<bool> vert_selection = attributes.lookup_or_default<bool>(
BKE_uv_map_vert_select_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
const VArray<bool> edge_selection = attributes.lookup_or_default<bool>(
BKE_uv_map_edge_select_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
const VArray<bool> pin = attributes.lookup_or_default<bool>(
BKE_uv_map_pin_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
const bool *vert_selection = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_vert_select_name_get(layer.name, buffer)));
const bool *edge_selection = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_edge_select_name_get(layer.name, buffer)));
const bool *pin = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_pin_name_get(layer.name, buffer)));
threading::parallel_for(mloopuv.index_range(), 2048, [&](IndexRange range) {
for (const int i : range) {
copy_v2_v2(mloopuv[i].uv, coords[i]);
SET_FLAG_FROM_TEST(mloopuv[i].flag, vert_selection[i], MLOOPUV_VERTSEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, edge_selection[i], MLOOPUV_EDGESEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, pin[i], MLOOPUV_PINNED);
SET_FLAG_FROM_TEST(mloopuv[i].flag, vert_selection && vert_selection[i], MLOOPUV_VERTSEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, edge_selection && edge_selection[i], MLOOPUV_EDGESEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, pin && pin[i], MLOOPUV_PINNED);
}
});
new_layer_to_write.append(mloopuv_layer);
}
/* #CustomData expects the layers to be sorted in increasing order based on type. */
std::stable_sort(
new_layer_to_write.begin(),
new_layer_to_write.end(),
[](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
loop_layers_to_write = new_layer_to_write;
mesh->ldata.totlayer = new_layer_to_write.size();
mesh->ldata.maxlayer = mesh->ldata.totlayer;

View File

@ -77,17 +77,17 @@ TEST(nla_track, BKE_nlatrack_remove_strip)
strip2.start = 11;
strip2.end = 20;
// Add NLA strips to the NLATrack.
/* Add NLA strips to the NLATrack. */
BKE_nlastrips_add_strip(&strips, &strip1);
BKE_nlastrips_add_strip(&strips, &strip2);
track.strips = strips;
// ensure we have 2 strips in the track.
/* Ensure we have 2 strips in the track. */
EXPECT_EQ(2, BLI_listbase_count(&track.strips));
BKE_nlatrack_remove_strip(&track, &strip2);
EXPECT_EQ(1, BLI_listbase_count(&track.strips));
// ensure the correct strip was removed.
/* Ensure the correct strip was removed. */
EXPECT_EQ(-1, BLI_findindex(&track.strips, &strip2));
}
@ -97,20 +97,20 @@ TEST(nla_track, BKE_nlatrack_remove_and_free)
NlaTrack *track1;
NlaTrack *track2;
// Add NLA tracks to the Animation Data.
/* Add NLA tracks to the Animation Data. */
track1 = BKE_nlatrack_add(&adt, NULL, false);
track2 = BKE_nlatrack_add(&adt, track1, false);
// ensure we have 2 tracks in the track.
/* Ensure we have 2 tracks in the track. */
EXPECT_EQ(2, BLI_listbase_count(&adt.nla_tracks));
BKE_nlatrack_remove_and_free(&adt.nla_tracks, track2, false);
EXPECT_EQ(1, BLI_listbase_count(&adt.nla_tracks));
// ensure the correct track was removed.
/* Ensure the correct track was removed. */
EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track2));
// free the rest of the tracks, and ensure they are removed.
/* Free the rest of the tracks, and ensure they are removed. */
BKE_nlatrack_remove_and_free(&adt.nla_tracks, track1, false);
EXPECT_EQ(0, BLI_listbase_count(&adt.nla_tracks));
EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track1));

View File

@ -2479,6 +2479,8 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
link.tosock->link = &link;
}
Vector<bNodeLink *> duplicate_links_to_remove;
/* redirect downstream links */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
/* do we have internal link? */
@ -2495,7 +2497,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
link_to_compare->tosock == link->tosock) {
adjust_multi_input_indices_after_removed_link(
ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index);
nodeRemLink(ntree, link_to_compare);
duplicate_links_to_remove.append_non_duplicates(link_to_compare);
}
}
}
@ -2533,6 +2535,10 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
}
}
for (bNodeLink *link : duplicate_links_to_remove) {
nodeRemLink(ntree, link);
}
/* remove remaining upstream links */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if (link->tonode == node) {

View File

@ -956,7 +956,7 @@ ScrArea *BKE_screen_area_map_find_area_xy(const ScrAreaMap *areamap,
const int xy[2])
{
LISTBASE_FOREACH (ScrArea *, area, &areamap->areabase) {
/* Test area's outer screen verts, not inner totrct. */
/* Test area's outer screen verts, not inner `area->totrct`. */
if (xy[0] >= area->v1->vec.x && xy[0] <= area->v4->vec.x && xy[1] >= area->v1->vec.y &&
xy[1] <= area->v2->vec.y) {
if (ELEM(spacetype, SPACE_TYPE_ANY, area->spacetype)) {

View File

@ -304,6 +304,13 @@ void rotate_eul(float beul[3], char axis, float angle);
/* Order independent. */
/**
* Manipulate `eul` so it's close to `oldrot` while representing the same rotation
* with the aim of having the minimum difference between all axes.
*
* This is typically done so interpolating the values between two euler rotations
* doesn't add undesired rotation (even rotating multiple times around one axis).
*/
void compatible_eul(float eul[3], const float oldrot[3]);
void add_eul_euleul(float r_eul[3], float a[3], float b[3], short order);

View File

@ -1491,14 +1491,17 @@ void rotate_eul(float beul[3], const char axis, const float angle)
void compatible_eul(float eul[3], const float oldrot[3])
{
/* When the rotation exceeds 180 degrees, it can be wrapped by 360 degrees
* to produce a closer match, see !104856. */
* to produce a closer match.
* NOTE: Values between `pi` & `2 * pi` work, where `pi` has the lowest number of
* discontinuities and values approaching `2 * pi` center the resulting rotation around zero,
* at the expense of the result being less compatible, see !104856. */
const float pi_thresh = (float)M_PI;
const float pi_x2 = (2.0f * (float)M_PI);
float deul[3];
uint i;
/* correct differences of about 360 degrees first */
/* Correct differences around 360 degrees first. */
for (i = 0; i < 3; i++) {
deul[i] = eul[i] - oldrot[i];
if (deul[i] > pi_thresh) {
@ -1513,8 +1516,9 @@ void compatible_eul(float eul[3], const float oldrot[3])
uint j = 1, k = 2;
for (i = 0; i < 3; j = k, k = i++) {
/* is 1 of the axis rotations larger than 180 degrees and the other small? */
if (fabsf(deul[i]) > 3.2f && fabsf(deul[j]) < 1.6f && fabsf(deul[k]) < 1.6f) {
/* Check if this axis of rotations larger than 180 degrees and
* the others are smaller than 90 degrees. */
if (fabsf(deul[i]) > M_PI && fabsf(deul[j]) < M_PI_2 && fabsf(deul[k]) < M_PI_2) {
if (deul[i] > 0.0f) {
eul[i] -= pi_x2;
}

View File

@ -59,9 +59,9 @@ void main()
* IF PrimType == LineList: base_vertex_id = quad_id*2
* IF PrimType == LineStrip: base_vertex_id = quad_id
*
* Note: This is currently used as LineList.
* NOTE: This is currently used as LineList.
*
* Note: Primitive Restart Will not work with this setup as-is. We should avoid using
* NOTE: Primitive Restart Will not work with this setup as-is. We should avoid using
* input primitive types which use restart indices. */
int base_vertex_id = quad_id * 2;

View File

@ -47,6 +47,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
#include "ED_keyframes_edit.h" /* XXX move the select modes out of there! */
#include "ED_markers.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
@ -3638,6 +3639,283 @@ static void ANIM_OT_channel_select_keys(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name View Channel Operator
* \{ */
static void get_normalized_fcurve_bounds(FCurve *fcu,
bAnimContext *ac,
const bAnimListElem *ale,
const bool include_handles,
const float range[2],
rctf *r_bounds)
{
const bool fcu_selection_only = false;
BKE_fcurve_calc_bounds(fcu,
&r_bounds->xmin,
&r_bounds->xmax,
&r_bounds->ymin,
&r_bounds->ymax,
fcu_selection_only,
include_handles,
range);
const short mapping_flag = ANIM_get_normalization_flags(ac);
const float min_height = 0.01f;
const float height = BLI_rctf_size_y(r_bounds);
if (height < min_height) {
r_bounds->ymin -= (min_height - height) / 2;
r_bounds->ymax += (min_height - height) / 2;
}
float offset;
const float unit_fac = ANIM_unit_mapping_get_factor(
ac->scene, ale->id, fcu, mapping_flag, &offset);
r_bounds->ymin = (r_bounds->ymin + offset) * unit_fac;
r_bounds->ymax = (r_bounds->ymax + offset) * unit_fac;
}
static void get_gpencil_bounds(bGPDlayer *gpl, const float range[2], rctf *r_bounds)
{
bool found_start = false;
int start_frame = 0;
int end_frame = 1;
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if (gpf->framenum < range[0]) {
continue;
}
if (gpf->framenum > range[1]) {
break;
}
if (!found_start) {
start_frame = gpf->framenum;
found_start = true;
}
end_frame = gpf->framenum;
}
r_bounds->xmin = start_frame;
r_bounds->xmax = end_frame;
r_bounds->ymin = 0;
r_bounds->ymax = 1;
}
static bool get_channel_bounds(bAnimContext *ac,
bAnimListElem *ale,
const float range[2],
const bool include_handles,
rctf *r_bounds)
{
bool found_bounds = false;
switch (ale->datatype) {
case ALE_GPFRAME: {
bGPDlayer *gpl = (bGPDlayer *)ale->data;
get_gpencil_bounds(gpl, range, r_bounds);
found_bounds = true;
break;
}
case ALE_FCURVE: {
FCurve *fcu = (FCurve *)ale->key_data;
get_normalized_fcurve_bounds(fcu, ac, ale, include_handles, range, r_bounds);
found_bounds = true;
break;
}
}
return found_bounds;
}
static void get_view_range(Scene *scene, const bool use_preview_range, float r_range[2])
{
if (use_preview_range && scene->r.flag & SCER_PRV_RANGE) {
r_range[0] = scene->r.psfra;
r_range[1] = scene->r.pefra;
}
else {
r_range[0] = -FLT_MAX;
r_range[1] = FLT_MAX;
}
}
/* Pad the given rctf with regions that could block the view.
* For example Markers and Time Scrubbing. */
static void add_region_padding(bContext *C, bAnimContext *ac, rctf *bounds)
{
BLI_rctf_scale(bounds, 1.1f);
const float pad_top = UI_TIME_SCRUB_MARGIN_Y;
const float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ?
V2D_SCROLL_HANDLE_HEIGHT :
UI_MARKER_MARGIN_Y;
BLI_rctf_pad_y(bounds, ac->region->winy, pad_bottom, pad_top);
}
/* Find the window region in the bAnimContext area and move it to bounds. */
static void move_graph_view(bContext *C, bAnimContext *ac, rctf *bounds, const int smooth_viewtx)
{
LISTBASE_FOREACH (ARegion *, region, &ac->area->regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
UI_view2d_smooth_view(C, region, bounds, smooth_viewtx);
}
}
}
static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
/* Get editor data. */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
ListBase anim_data = {NULL, NULL};
const int filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_DATA_VISIBLE |
ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
size_t anim_data_length = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
if (anim_data_length == 0) {
WM_report(RPT_WARNING, "No channels to operate on");
return OPERATOR_CANCELLED;
}
float range[2];
const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
get_view_range(ac.scene, use_preview_range, range);
rctf bounds = {.xmin = FLT_MAX, .xmax = -FLT_MAX, .ymin = FLT_MAX, .ymax = -FLT_MAX};
bAnimListElem *ale;
const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
bool valid_bounds = false;
for (ale = anim_data.first; ale; ale = ale->next) {
rctf channel_bounds;
const bool found_bounds = get_channel_bounds(
&ac, ale, range, include_handles, &channel_bounds);
if (found_bounds) {
BLI_rctf_union(&bounds, &channel_bounds);
valid_bounds = true;
}
}
if (!valid_bounds) {
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
add_region_padding(C, &ac, &bounds);
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
move_graph_view(C, &ac, &bounds, smooth_viewtx);
ANIM_animdata_freelist(&anim_data);
return OPERATOR_FINISHED;
}
static bool channel_view_poll(bContext *C)
{
return ED_operator_action_active(C) || ED_operator_graphedit_active(C);
}
static void ANIM_OT_channels_view_selected(wmOperatorType *ot)
{
/* Identifiers */
ot->name = "Frame Selected Channels";
ot->idname = "ANIM_OT_channels_view_selected";
ot->description = "Reset viewable area to show the selected channels";
/* API callbacks */
ot->exec = graphkeys_view_selected_channels_exec;
ot->poll = channel_view_poll;
ot->flag = 0;
ot->prop = RNA_def_boolean(ot->srna,
"include_handles",
true,
"Include Handles",
"Include handles of keyframes when calculating extents");
ot->prop = RNA_def_boolean(ot->srna,
"use_preview_range",
true,
"Use Preview Range",
"Ignore frames outside of the preview range");
}
static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
ListBase anim_data = {NULL, NULL};
const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS |
ANIMFILTER_LIST_CHANNELS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
bAnimListElem *ale;
const int channel_index = animchannels_channel_get(&ac, event->mval);
ale = BLI_findlink(&anim_data, channel_index);
if (ale == NULL) {
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
float range[2];
const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
get_view_range(ac.scene, use_preview_range, range);
rctf bounds;
const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
const bool found_bounds = get_channel_bounds(&ac, ale, range, include_handles, &bounds);
if (!found_bounds) {
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
add_region_padding(C, &ac, &bounds);
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
move_graph_view(C, &ac, &bounds, smooth_viewtx);
ANIM_animdata_freelist(&anim_data);
return OPERATOR_FINISHED;
}
static void ANIM_OT_channel_view_pick(wmOperatorType *ot)
{
/* Identifiers */
ot->name = "Frame Channel Under Cursor";
ot->idname = "ANIM_OT_channel_view_pick";
ot->description = "Reset viewable area to show the channel under the cursor";
/* API callbacks */
ot->invoke = graphkeys_channel_view_pick_invoke;
ot->poll = channel_view_poll;
ot->flag = 0;
ot->prop = RNA_def_boolean(ot->srna,
"include_handles",
true,
"Include Handles",
"Include handles of keyframes when calculating extents");
ot->prop = RNA_def_boolean(ot->srna,
"use_preview_range",
true,
"Use Preview Range",
"Ignore frames outside of the preview range");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operator Registration
* \{ */
@ -3657,6 +3935,9 @@ void ED_operatortypes_animchannels(void)
WM_operatortype_append(ANIM_OT_channels_setting_disable);
WM_operatortype_append(ANIM_OT_channels_setting_toggle);
WM_operatortype_append(ANIM_OT_channel_view_pick);
WM_operatortype_append(ANIM_OT_channels_view_selected);
WM_operatortype_append(ANIM_OT_channels_delete);
/* XXX does this need to be a separate operator? */

View File

@ -1429,7 +1429,7 @@ static int separate_exec(bContext *C, wmOperator *op)
/* All curves failed due to the same error. */
if (status.error_vertex_keys) {
BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with vertex keys");
BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with shape keys");
}
else {
BLI_assert(status.error_generic);

View File

@ -285,7 +285,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op)
params.add_solidify = RNA_boolean_get(op->ptr, "add_solidify");
/* Push an undo step prior to extraction.
* Note: A second push happens after the operator due to
* NOTE: A second push happens after the operator due to
* the OPTYPE_UNDO flag; having an initial undo step here
* is just needed to preserve the active object pointer.
*

View File

@ -400,6 +400,21 @@ static void saction_channel_region_message_subscribe(const wmRegionMessageSubscr
}
}
static void action_clamp_scroll(ARegion *region)
{
View2D *v2d = &region->v2d;
const float cur_height_y = BLI_rctf_size_y(&v2d->cur);
if (BLI_rctf_size_y(&v2d->cur) > BLI_rctf_size_y(&v2d->tot)) {
v2d->cur.ymin = -cur_height_y;
v2d->cur.ymax = 0;
}
else if (v2d->cur.ymin < v2d->tot.ymin) {
v2d->cur.ymin = v2d->tot.ymin;
v2d->cur.ymax = v2d->cur.ymin + cur_height_y;
}
}
static void action_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
@ -860,6 +875,13 @@ static void action_blend_write(BlendWriter *writer, SpaceLink *sl)
BLO_write_struct(writer, SpaceAction, sl);
}
static void action_main_region_view2d_changed(const bContext *UNUSED(C), ARegion *region)
{
/* V2D_KEEPTOT_STRICT cannot be used to clamp scrolling
* because it also clamps the x-axis to 0. */
action_clamp_scroll(region);
}
void ED_spacetype_action(void)
{
SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype action");
@ -892,6 +914,7 @@ void ED_spacetype_action(void)
art->draw_overlay = action_main_region_draw_overlay;
art->listener = action_main_region_listener;
art->message_subscribe = saction_main_region_message_subscribe;
art->on_view2d_changed = action_main_region_view2d_changed;
art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
BLI_addhead(&st->regiontypes, art);

View File

@ -785,7 +785,12 @@ void uiTemplateMovieclipInformation(uiLayout *layout,
uiLayout *col = uiLayoutColumn(layout, false);
uiLayoutSetAlignment(col, UI_LAYOUT_ALIGN_RIGHT);
ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, MOVIECLIP_CACHE_SKIP);
/* NOTE: Put the frame to cache. If the panel is drawn, the display will also be shown, as well
* as metadata panel. So if the cache is skipped here it is not really a memory saver, but
* skipping the cache could lead to a performance impact depending on the order in which panels
* and the main area is drawn. Basically, if it is this template drawn first and then the main
* area it will lead to frame read and processing happening twice. */
ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, 0);
int width, height;
/* Display frame dimensions, channels number and buffer type. */

View File

@ -1880,6 +1880,11 @@ static bool euler_filter_single_channel(FCurve *fcu)
return false;
}
/* Skip baked FCurves. */
if (fcu->bezt == NULL) {
return false;
}
/* `prev` follows bezt, bezt = "current" point to be fixed. */
/* Our method depends on determining a "difference" from the previous vert. */
bool is_modified = false;

View File

@ -91,7 +91,7 @@ void get_graph_keyframe_extents(bAnimContext *ac,
/* Get range. */
if (BKE_fcurve_calc_bounds(
fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles)) {
fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles, NULL)) {
short mapping_flag = ANIM_get_normalization_flags(ac);
/* Apply NLA scaling. */

View File

@ -2680,7 +2680,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
Editing *ed = SEQ_editing_get(scene);
SpaceSeq *sseq = CTX_wm_space_seq(C);
View2D *v2d = &region->v2d;
float col[3];
seq_prefetch_wm_notify(C, scene);
@ -2689,8 +2688,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
GPU_depth_test(GPU_DEPTH_NONE);
UI_GetThemeColor3fv(TH_BACK, col);
GPU_clear_color(col[0], col[1], col[2], 0.0f);
UI_ThemeClearColor(TH_BACK);
UI_view2d_view_ortho(v2d);
draw_seq_timeline_channels(v2d);

View File

@ -68,6 +68,122 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
return std::make_unique<ColumnValues>(column_id.name, GVArray::ForSpan(*values));
}
static void add_mesh_debug_column_names(
const Mesh &mesh,
const eAttrDomain domain,
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn)
{
switch (domain) {
case ATTR_DOMAIN_POINT:
if (CustomData_has_layer(&mesh.vdata, CD_ORIGINDEX)) {
fn({(char *)"Original Index"}, false);
}
break;
case ATTR_DOMAIN_EDGE:
if (CustomData_has_layer(&mesh.edata, CD_ORIGINDEX)) {
fn({(char *)"Original Index"}, false);
}
fn({(char *)"Vertex 1"}, false);
fn({(char *)"Vertex 2"}, false);
break;
case ATTR_DOMAIN_FACE:
if (CustomData_has_layer(&mesh.pdata, CD_ORIGINDEX)) {
fn({(char *)"Original Index"}, false);
}
fn({(char *)"Corner Start"}, false);
fn({(char *)"Corner Size"}, false);
break;
case ATTR_DOMAIN_CORNER:
fn({(char *)"Vertex"}, false);
fn({(char *)"Edge"}, false);
break;
default:
BLI_assert_unreachable();
break;
}
}
static std::unique_ptr<ColumnValues> build_mesh_debug_columns(const Mesh &mesh,
const eAttrDomain domain,
const StringRef name)
{
switch (domain) {
case ATTR_DOMAIN_POINT: {
if (name == "Original Index") {
const int *data = static_cast<const int *>(
CustomData_get_layer(&mesh.vdata, CD_ORIGINDEX));
if (data) {
return std::make_unique<ColumnValues>(name, VArray<int>::ForSpan({data, mesh.totvert}));
}
}
return {};
}
case ATTR_DOMAIN_EDGE: {
const Span<MEdge> edges = mesh.edges();
if (name == "Original Index") {
const int *data = static_cast<const int *>(
CustomData_get_layer(&mesh.edata, CD_ORIGINDEX));
if (data) {
return std::make_unique<ColumnValues>(name, VArray<int>::ForSpan({data, mesh.totedge}));
}
}
if (name == "Vertex 1") {
return std::make_unique<ColumnValues>(
name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
return edges[index].v1;
}));
}
if (name == "Vertex 2") {
return std::make_unique<ColumnValues>(
name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
return edges[index].v2;
}));
}
return {};
}
case ATTR_DOMAIN_FACE: {
const Span<MPoly> polys = mesh.polys();
if (name == "Original Index") {
const int *data = static_cast<const int *>(
CustomData_get_layer(&mesh.pdata, CD_ORIGINDEX));
if (data) {
return std::make_unique<ColumnValues>(name, VArray<int>::ForSpan({data, mesh.totpoly}));
}
}
if (name == "Corner Start") {
return std::make_unique<ColumnValues>(
name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
return polys[index].loopstart;
}));
}
if (name == "Corner Size") {
return std::make_unique<ColumnValues>(
name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
return polys[index].totloop;
}));
}
return {};
}
case ATTR_DOMAIN_CORNER: {
const Span<MLoop> loops = mesh.loops();
if (name == "Vertex") {
return std::make_unique<ColumnValues>(
name,
VArray<int>::ForFunc(loops.size(), [loops](int64_t index) { return loops[index].v; }));
}
if (name == "Edge") {
return std::make_unique<ColumnValues>(
name,
VArray<int>::ForFunc(loops.size(), [loops](int64_t index) { return loops[index].e; }));
}
return {};
}
default:
BLI_assert_unreachable();
return {};
}
}
void GeometryDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
@ -109,17 +225,9 @@ void GeometryDataSource::foreach_default_column_ids(
fn({(char *)"Scale"}, false);
}
else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) {
if (domain_ == ATTR_DOMAIN_EDGE) {
fn({(char *)"Vertex 1"}, false);
fn({(char *)"Vertex 2"}, false);
}
else if (domain_ == ATTR_DOMAIN_FACE) {
fn({(char *)"Corner Start"}, false);
fn({(char *)"Corner Size"}, false);
}
else if (domain_ == ATTR_DOMAIN_CORNER) {
fn({(char *)"Vertex"}, false);
fn({(char *)"Edge"}, false);
const MeshComponent &component = static_cast<const MeshComponent &>(*component_);
if (const Mesh *mesh = component.get_for_read()) {
add_mesh_debug_column_names(*mesh, domain_, fn);
}
}
}
@ -174,51 +282,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) {
const MeshComponent &component = static_cast<const MeshComponent &>(*component_);
if (const Mesh *mesh = component.get_for_read()) {
const Span<MEdge> edges = mesh->edges();
const Span<MPoly> polys = mesh->polys();
const Span<MLoop> loops = mesh->loops();
if (domain_ == ATTR_DOMAIN_EDGE) {
if (STREQ(column_id.name, "Vertex 1")) {
return std::make_unique<ColumnValues>(
column_id.name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
return edges[index].v1;
}));
}
if (STREQ(column_id.name, "Vertex 2")) {
return std::make_unique<ColumnValues>(
column_id.name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
return edges[index].v2;
}));
}
}
else if (domain_ == ATTR_DOMAIN_FACE) {
if (STREQ(column_id.name, "Corner Start")) {
return std::make_unique<ColumnValues>(
column_id.name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
return polys[index].loopstart;
}));
}
if (STREQ(column_id.name, "Corner Size")) {
return std::make_unique<ColumnValues>(
column_id.name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
return polys[index].totloop;
}));
}
}
else if (domain_ == ATTR_DOMAIN_CORNER) {
if (STREQ(column_id.name, "Vertex")) {
return std::make_unique<ColumnValues>(
column_id.name, VArray<int>::ForFunc(loops.size(), [loops](int64_t index) {
return loops[index].v;
}));
}
if (STREQ(column_id.name, "Edge")) {
return std::make_unique<ColumnValues>(
column_id.name, VArray<int>::ForFunc(loops.size(), [loops](int64_t index) {
return loops[index].e;
}));
}
if (std::unique_ptr<ColumnValues> values = build_mesh_debug_columns(
*mesh, domain_, column_id.name)) {
return values;
}
}
}

View File

@ -1499,7 +1499,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
const int totedge = mesh.totedge;
/* Reuse the same buffer as #vert_dest_map.
* Note: the caller must be made aware of it changes. */
* NOTE: the caller must be made aware of it changes. */
MutableSpan<int> vert_group_map = vert_dest_map;
WeldMesh weld_mesh;

View File

@ -1895,7 +1895,7 @@ static void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length)
ai, ai + length, [](const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) {
int a = p1.v1 - p2.v1;
int b = p1.v2 - p2.v2;
/* parallel_sort() requires cmp() to return true when the first element needs to appear
/* `parallel_sort()` requires `cmp()` to return true when the first element needs to appear
* before the second element in the sorted array, false otherwise (strict weak ordering),
* see https://en.cppreference.com/w/cpp/named_req/Compare. */
if (a < 0) {

View File

@ -346,7 +346,7 @@ void gpu_shader_create_info_init()
overlay_motion_path_line_clipped = overlay_motion_path_line_clipped_no_geom;
/* Workbench shadows.
* Note: Updates additional-info used by workbench shadow permutations.
* NOTE: Updates additional-info used by workbench shadow permutations.
* Must be prepared prior to permutation preparation. */
workbench_shadow_manifold = workbench_shadow_manifold_no_geom;
workbench_shadow_no_manifold = workbench_shadow_no_manifold_no_geom;

View File

@ -339,7 +339,7 @@ void MTLImmediate::end()
} break;
case GPU_PRIM_LINE_LOOP: {
/* Patch final vertex of line loop to close. Rendered using LineStrip.
* Note: vertex_len represents original length, however, allocated Metal
* NOTE: vertex_len represents original length, however, allocated Metal
* buffer contains space for one extra vertex when LineLoop is used. */
uchar *buffer_data = reinterpret_cast<uchar *>(current_allocation_.data);
memcpy(buffer_data + (vertex_len)*vertex_format.stride,

View File

@ -41,7 +41,18 @@ void GLIndexBuf::bind()
void GLIndexBuf::bind_as_ssbo(uint binding)
{
bind();
if (ibo_id_ == 0 || data_ != nullptr) {
/* Calling `glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_)` changes the index buffer
* of the currently bound VAO.
*
* In the OpenGL backend, the VAO state persists even after `GLVertArray::update_bindings`
* is called.
*
* NOTE: For safety, we could call `glBindVertexArray(0)` right after drawing a `GPUBatch`.
* However, for performance reasons, we have chosen not to do so. */
glBindVertexArray(0);
bind();
}
BLI_assert(ibo_id_ != 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_);
}

View File

@ -379,6 +379,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
result = BKE_mesh_new_nomain_from_template(
mesh, int(maxVerts), int(maxEdges), 0, int(maxPolys) * 4, int(maxPolys));
/* The modifier doesn't support original index mapping on the edge or face domains. Remove
* original index layers, since otherwise edges aren't displayed at all in wireframe view. */
CustomData_free_layers(&result->edata, CD_ORIGINDEX, result->totedge);
CustomData_free_layers(&result->pdata, CD_ORIGINDEX, result->totedge);
const float(*vert_positions_orig)[3] = BKE_mesh_vert_positions(mesh);
const MEdge *medge_orig = BKE_mesh_edges(mesh);

View File

@ -394,7 +394,7 @@ PyTypeObject PyKDTree_Type = {
/*tp_setattro*/ NULL,
/*tp_as_buffer*/ NULL,
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
/*Documentation string*/ py_KDtree_doc,
/*tp_doc*/ py_KDtree_doc,
/*tp_traverse*/ NULL,
/*tp_clear*/ NULL,
/*tp_richcompare*/ NULL,

View File

@ -1119,9 +1119,28 @@ void wm_gizmomap_modal_set(
}
if (do_refresh) {
const int update_flag = GIZMOMAP_IS_REFRESH_CALLBACK;
const eWM_GizmoFlagMapDrawStep step = WM_gizmomap_drawstep_from_gizmo_group(
gz->parent_gzgroup);
gzmap->update_flag[step] |= GIZMOMAP_IS_REFRESH_CALLBACK;
gzmap->update_flag[step] |= update_flag;
/* Ensure the update flag is set for gizmos that were hidden while modal, see #104817. */
for (int i = 0; i < WM_GIZMOMAP_DRAWSTEP_MAX; i++) {
const eWM_GizmoFlagMapDrawStep step_iter = (eWM_GizmoFlagMapDrawStep)i;
if (step_iter == step) {
continue;
}
if ((gzmap->update_flag[i] & update_flag) == update_flag) {
continue;
}
LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
if (((gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) &&
wm_gizmogroup_is_visible_in_drawstep(gzgroup, step_iter)) {
gzmap->update_flag[i] |= update_flag;
break;
}
}
}
}
}