Vulkan: Push constants #104880

Merged
Jeroen Bakker merged 73 commits from Jeroen-Bakker/blender:vulkan-push-constants into main 2023-03-06 12:29:06 +01:00
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;
}
}
}
}
}