UI: "vertex keys" -> "shape keys" #104868

Closed
Philipp Oeser wants to merge 38 commits from lichtwerk:cleanup_rename_vertex_keys into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
89 changed files with 1793 additions and 856 deletions

View File

@ -1,8 +0,0 @@
{
"project_id" : "Blender",
"conduit_uri" : "https://developer.blender.org/",
"phabricator.uri" : "https://developer.blender.org/",
"git.default-relative-commit" : "origin/master",
"arc.land.update.default" : "rebase",
"arc.land.onto.default" : "master"
}

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
## Update and uncomment this in the release branch
set(BLENDER_VERSION 3.5)
# set(BLENDER_VERSION 3.1)
function(download_source dep)
set(TARGET_FILE ${${dep}_FILE})

View File

@ -10,7 +10,7 @@ ExternalProject_Add(external_epoxy
URL_HASH ${EPOXY_HASH_TYPE}=${EPOXY_HASH}
PREFIX ${BUILD_DIR}/epoxy
PATCH_COMMAND ${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/epoxy/src/external_epoxy/ < ${PATCH_DIR}/epoxy.diff
CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false
CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false ${MESON_BUILD_TYPE}
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
)

View File

@ -9,7 +9,7 @@ ExternalProject_Add(external_fribidi
URL_HASH ${FRIBIDI_HASH_TYPE}=${FRIBIDI_HASH}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
PREFIX ${BUILD_DIR}/fribidi
CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi
CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi ${MESON_BUILD_TYPE} -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
INSTALL_DIR ${LIBDIR}/fribidi

View File

@ -21,6 +21,7 @@ set(HARFBUZZ_EXTRA_OPTIONS
# Only used for command line utilities,
# disable as this would add an addition & unnecessary build-dependency.
-Dcairo=disabled
${MESON_BUILD_TYPE}
)
ExternalProject_Add(external_harfbuzz
@ -59,3 +60,10 @@ if(BUILD_MODE STREQUAL Release AND WIN32)
DEPENDEES install
)
endif()
if(BUILD_MODE STREQUAL Debug AND WIN32)
ExternalProject_Add_Step(external_harfbuzz after_install
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/harfbuzz/lib/libharfbuzz.a ${HARVEST_TARGET}/harfbuzz/lib/libharfbuzz_d.lib
DEPENDEES install
)
endif()

View File

@ -15,7 +15,7 @@ llvm-config = '${LIBDIR}/llvm/bin/llvm-config'"
)
set(MESA_EXTRA_FLAGS
-Dbuildtype=release
${MESON_BUILD_TYPE}
-Dc_args=${MESA_CFLAGS}
-Dcpp_args=${MESA_CXXFLAGS}
-Dc_link_args=${MESA_LDFLAGS}

View File

@ -16,8 +16,10 @@ message("BuildMode = ${BUILD_MODE}")
if(BUILD_MODE STREQUAL "Debug")
set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Debug)
set(MESON_BUILD_TYPE -Dbuildtype=debug)
else()
set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Release)
set(MESON_BUILD_TYPE -Dbuildtype=release)
endif()
set(DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloads" CACHE STRING "Path for downloaded files")

View File

@ -13,7 +13,7 @@ ExternalProject_Add(external_wayland
# NOTE: `-lm` is needed for `libxml2` which is a static library that uses `libm.so`,
# without this, math symbols such as `floor` aren't found.
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/expat/lib/pkgconfig:${LIBDIR}/xml2/lib/pkgconfig:${LIBDIR}/ffi/lib/pkgconfig:$PKG_CONFIG_PATH
${MESON} --prefix ${LIBDIR}/wayland -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland
${MESON} --prefix ${LIBDIR}/wayland ${MESON_BUILD_TYPE} -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
)

View File

@ -7,7 +7,7 @@ ExternalProject_Add(external_wayland_protocols
PREFIX ${BUILD_DIR}/wayland-protocols
# Use `-E` so the `PKG_CONFIG_PATH` can be defined to link against our own WAYLAND.
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/wayland/lib64/pkgconfig:$PKG_CONFIG_PATH
${MESON} --prefix ${LIBDIR}/wayland-protocols . ../external_wayland_protocols -Dtests=false
${MESON} --prefix ${LIBDIR}/wayland-protocols ${MESON_BUILD_TYPE} . ../external_wayland_protocols -Dtests=false
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
)

View File

@ -85,7 +85,7 @@ if(NOT APPLE)
set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE)
set(WITH_CYCLES_HIP_BINARIES ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_HIP_BINARIES OFF CACHE BOOL "" FORCE)
set(WITH_CYCLES_DEVICE_ONEAPI ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_ONEAPI_BINARIES ON CACHE BOOL "" FORCE)
endif()

View File

@ -5,38 +5,38 @@
update-code:
git:
submodules:
- branch: blender-v3.5-release
- branch: main
commit_id: HEAD
path: release/scripts/addons
- branch: blender-v3.5-release
- branch: main
commit_id: HEAD
path: release/scripts/addons_contrib
- branch: blender-v3.5-release
- branch: main
commit_id: HEAD
path: release/datafiles/locale
- branch: blender-v3.5-release
- branch: main
commit_id: HEAD
path: source/tools
svn:
libraries:
darwin-arm64:
branch: tags/blender-3.5-release
branch: trunk
commit_id: HEAD
path: lib/darwin_arm64
darwin-x86_64:
branch: tags/blender-3.5-release
branch: trunk
commit_id: HEAD
path: lib/darwin
linux-x86_64:
branch: tags/blender-3.5-release
branch: trunk
commit_id: HEAD
path: lib/linux_x86_64_glibc_228
windows-amd64:
branch: tags/blender-3.5-release
branch: trunk
commit_id: HEAD
path: lib/win64_vc15
tests:
branch: tags/blender-3.5-release
branch: trunk
commit_id: HEAD
path: lib/tests
benchmarks:

View File

@ -38,7 +38,7 @@ PROJECT_NAME = Blender
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = V3.5
PROJECT_NUMBER = V3.6
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@ -1676,17 +1676,20 @@ class CyclesPreferences(bpy.types.AddonPreferences):
col.label(text="and NVIDIA driver version %s or newer" % driver_version,
icon='BLANK1', translate=False)
elif device_type == 'HIP':
import sys
if sys.platform[:3] == "win":
driver_version = "21.Q4"
col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1')
col.label(text=iface_("and AMD Radeon Pro %s driver or newer") % driver_version,
icon='BLANK1', translate=False)
elif sys.platform.startswith("linux"):
driver_version = "22.10"
col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1')
col.label(text=iface_("and AMD driver version %s or newer") % driver_version, icon='BLANK1',
translate=False)
if True:
col.label(text="HIP temporarily disabled due to compiler bugs", icon='BLANK1')
else:
import sys
if sys.platform[:3] == "win":
driver_version = "21.Q4"
col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1')
col.label(text=iface_("and AMD Radeon Pro %s driver or newer") % driver_version,
icon='BLANK1', translate=False)
elif sys.platform.startswith("linux"):
driver_version = "22.10"
col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1')
col.label(text=iface_("and AMD driver version %s or newer") % driver_version, icon='BLANK1',
translate=False)
elif device_type == 'ONEAPI':
import sys
if sys.platform.startswith("win"):

View File

@ -42,12 +42,15 @@ endif()
###########################################################################
if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP)
find_package(HIP)
set_and_warn_library_found("HIP compiler" HIP_FOUND WITH_CYCLES_HIP_BINARIES)
set(WITH_CYCLES_HIP_BINARIES OFF)
message(STATUS "HIP temporarily disabled due to compiler bugs")
if(HIP_FOUND)
message(STATUS "Found HIP ${HIP_HIPCC_EXECUTABLE} (${HIP_VERSION})")
endif()
# find_package(HIP)
# set_and_warn_library_found("HIP compiler" HIP_FOUND WITH_CYCLES_HIP_BINARIES)
# if(HIP_FOUND)
# message(STATUS "Found HIP ${HIP_HIPCC_EXECUTABLE} (${HIP_VERSION})")
# endif()
endif()
if(NOT WITH_HIP_DYNLOAD)

View File

@ -306,14 +306,23 @@ static void gwl_window_frame_update_from_pending(GWL_Window *win);
#ifdef USE_EVENT_BACKGROUND_THREAD
enum eGWL_PendingWindowActions {
PENDING_FRAME_CONFIGURE = 0,
PENDING_EGL_RESIZE,
/**
* The state of the window frame has changed, apply the state from #GWL_Window::frame_pending.
*/
PENDING_WINDOW_FRAME_CONFIGURE = 0,
/** The EGL buffer must be resized to match #GWL_WindowFrame::size. */
PENDING_EGL_WINDOW_RESIZE,
# ifdef GHOST_OPENGL_ALPHA
/** Draw an opaque region behind the window. */
PENDING_OPAQUE_SET,
# endif
PENDING_SCALE_UPDATE,
/**
* The DPI for a monitor has changed or the monitors (outputs)
* this window is visible on may have changed. Recalculate the windows scale.
*/
PENDING_OUTPUT_SCALE_UPDATE,
};
# define PENDING_NUM (PENDING_SCALE_UPDATE + 1)
# define PENDING_NUM (PENDING_OUTPUT_SCALE_UPDATE + 1)
static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWindowActions type)
{
@ -323,10 +332,10 @@ static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWin
static void gwl_window_pending_actions_handle(GWL_Window *win)
{
if (win->pending_actions[PENDING_FRAME_CONFIGURE].exchange(false)) {
if (win->pending_actions[PENDING_WINDOW_FRAME_CONFIGURE].exchange(false)) {
gwl_window_frame_update_from_pending(win);
}
if (win->pending_actions[PENDING_EGL_RESIZE].exchange(false)) {
if (win->pending_actions[PENDING_EGL_WINDOW_RESIZE].exchange(false)) {
wl_egl_window_resize(win->egl_window, UNPACK2(win->frame.size), 0, 0);
}
# ifdef GHOST_OPENGL_ALPHA
@ -334,7 +343,7 @@ static void gwl_window_pending_actions_handle(GWL_Window *win)
win->ghost_window->setOpaque();
}
# endif
if (win->pending_actions[PENDING_SCALE_UPDATE].exchange(false)) {
if (win->pending_actions[PENDING_OUTPUT_SCALE_UPDATE].exchange(false)) {
win->ghost_window->outputs_changed_update_scale();
}
}
@ -342,9 +351,10 @@ static void gwl_window_pending_actions_handle(GWL_Window *win)
#endif /* USE_EVENT_BACKGROUND_THREAD */
/**
* Update the window's #GWL_WindowFrame
* Update the window's #GWL_WindowFrame.
* The caller must handle locking & run from the main thread.
*/
static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
{
#ifdef USE_EVENT_BACKGROUND_THREAD
GHOST_ASSERT(win->ghost_system->main_thread_id == std::this_thread::get_id(),
@ -381,7 +391,7 @@ static void gwl_window_frame_update_from_pending(GWL_Window *win)
#ifdef USE_EVENT_BACKGROUND_THREAD
std::lock_guard lock_frame_guard{win->frame_pending_mutex};
#endif
gwl_window_frame_update_from_pending_lockfree(win);
gwl_window_frame_update_from_pending_no_lock(win);
}
/** \} */
@ -576,12 +586,12 @@ static void frame_handle_configure(struct libdecor_frame *frame,
GHOST_SystemWayland *system = win->ghost_system;
const bool is_main_thread = system->main_thread_id == std::this_thread::get_id();
if (!is_main_thread) {
gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE);
gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE);
}
else
# endif
{
gwl_window_frame_update_from_pending_lockfree(win);
gwl_window_frame_update_from_pending_no_lock(win);
}
}
}
@ -671,7 +681,7 @@ static void xdg_surface_handle_configure(void *data,
if (!is_main_thread) {
/* NOTE(@ideasman42): this only gets one redraw,
* I could not find a case where this causes problems. */
gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE);
gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE);
}
else
#endif
@ -1373,7 +1383,7 @@ bool GHOST_WindowWayland::outputs_changed_update_scale()
{
#ifdef USE_EVENT_BACKGROUND_THREAD
if (system_->main_thread_id != std::this_thread::get_id()) {
gwl_window_pending_actions_tag(window_, PENDING_SCALE_UPDATE);
gwl_window_pending_actions_tag(window_, PENDING_OUTPUT_SCALE_UPDATE);
return false;
}
#endif

View File

@ -93,7 +93,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
}
RECT win_rect = {left, top, long(left + width), long(top + height)};
adjustWindowRectForClosestMonitor(&win_rect, style, extended_style);
adjustWindowRectForDesktop(&win_rect, style, extended_style);
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
m_hWnd = ::CreateWindowExW(extended_style, /* window extended style */
@ -298,24 +298,52 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
m_directManipulationHelper = NULL;
}
void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
DWORD dwStyle,
DWORD dwExStyle)
void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyle, DWORD dwExStyle)
{
/* Get Details of the closest monitor. */
HMONITOR hmonitor = MonitorFromRect(win_rect, MONITOR_DEFAULTTONEAREST);
/* Windows can span multiple monitors, but must be usable. The desktop can have a larger
* surface than all monitors combined, for example when two monitors are aligned diagonally.
* Therefore we ensure that all the window's corners are within some monitor's Work area. */
POINT pt;
HMONITOR hmonitor;
MONITORINFOEX monitor;
monitor.cbSize = sizeof(MONITORINFOEX);
monitor.dwFlags = 0;
GetMonitorInfo(hmonitor, &monitor);
/* Constrain requested size and position to fit within this monitor. */
LONG width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect->right - win_rect->left);
LONG height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect->bottom - win_rect->top);
win_rect->left = min(max(monitor.rcWork.left, win_rect->left), monitor.rcWork.right - width);
win_rect->right = win_rect->left + width;
win_rect->top = min(max(monitor.rcWork.top, win_rect->top), monitor.rcWork.bottom - height);
win_rect->bottom = win_rect->top + height;
/* 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. */
/* Top-left. */
pt.x = win_rect->left;
pt.y = win_rect->top;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->top = max(win_rect->top, monitor.rcWork.top);
win_rect->left = max(win_rect->left, monitor.rcWork.left);
/* Top-right. */
pt.x = win_rect->right;
pt.y = win_rect->top;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->top = max(win_rect->top, monitor.rcWork.top);
win_rect->right = min(win_rect->right, monitor.rcWork.right);
/* Bottom-left. */
pt.x = win_rect->left;
pt.y = win_rect->bottom;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->bottom = min(win_rect->bottom, monitor.rcWork.bottom);
win_rect->left = max(win_rect->left, monitor.rcWork.left);
/* Bottom-right. */
pt.x = win_rect->right;
pt.y = win_rect->bottom;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->bottom = min(win_rect->bottom, monitor.rcWork.bottom);
win_rect->right = min(win_rect->right, monitor.rcWork.right);
/* With Windows 10 and newer we can adjust for chrome that differs with DPI and scale. */
GHOST_WIN32_AdjustWindowRectExForDpi fpAdjustWindowRectExForDpi = nullptr;
@ -334,9 +362,6 @@ void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
else {
AdjustWindowRectEx(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle);
}
/* But never allow a top position that can hide part of the title bar. */
win_rect->top = max(monitor.rcWork.top, win_rect->top);
}
bool GHOST_WindowWin32::getValid() const

View File

@ -87,12 +87,12 @@ class GHOST_WindowWin32 : public GHOST_Window {
~GHOST_WindowWin32();
/**
* Adjusts a requested window rect to fit and position correctly in monitor.
* Adjusts a requested window rect to fit and position within the desktop.
* \param win_rect: pointer to rectangle that will be modified.
* \param dwStyle: The Window Style of the window whose required size is to be calculated.
* \param dwExStyle: The Extended Window Style of the window.
*/
void adjustWindowRectForClosestMonitor(LPRECT win_rect, DWORD dwStyle, DWORD dwExStyle);
void adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyle, DWORD dwExStyle);
/**
* Returns indication as to whether the window is valid.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 678 KiB

After

Width:  |  Height:  |  Size: 823 KiB

View File

@ -5631,6 +5631,8 @@ def km_curves(params):
("curves.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("curves.delete", {"type": 'X', "value": 'PRESS'}, None),
("curves.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("curves.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("curves.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
])
return keymap

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

@ -604,7 +604,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
colliding_names = []
for collection in (
# Built-in names.
{"shade_smooth": None, "normal": None, "crease": None},
{"shade_smooth": None, "crease": None},
mesh.attributes,
None if ob is None else ob.vertex_groups,
):

View File

@ -2044,6 +2044,16 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
layout.operator("paint.vert_select_linked", text="Select Linked")
class VIEW3D_MT_edit_curves_select_more_less(Menu):
bl_label = "Select More/Less"
def draw(self, _context):
layout = self.layout
layout.operator("curves.select_more", text="More")
layout.operator("curves.select_less", text="Less")
class VIEW3D_MT_select_edit_curves(Menu):
bl_label = "Select"
@ -2053,10 +2063,17 @@ class VIEW3D_MT_select_edit_curves(Menu):
layout.operator("curves.select_all", text="All").action = 'SELECT'
layout.operator("curves.select_all", text="None").action = 'DESELECT'
layout.operator("curves.select_all", text="Invert").action = 'INVERT'
layout.separator()
layout.operator("curves.select_random", text="Random")
layout.operator("curves.select_end", text="Endpoints")
layout.operator("curves.select_linked", text="Linked")
layout.separator()
layout.menu("VIEW3D_MT_edit_curves_select_more_less")
class VIEW3D_MT_select_sculpt_curves(Menu):
bl_label = "Select"
@ -8046,6 +8063,7 @@ classes = (
VIEW3D_MT_select_gpencil,
VIEW3D_MT_select_paint_mask,
VIEW3D_MT_select_paint_mask_vertex,
VIEW3D_MT_edit_curves_select_more_less,
VIEW3D_MT_select_edit_curves,
VIEW3D_MT_select_sculpt_curves,
VIEW3D_MT_mesh_add,

View File

@ -41,6 +41,7 @@ typedef enum eAttrDomainMask {
ATTR_DOMAIN_MASK_CURVE = (1 << 4),
ATTR_DOMAIN_MASK_ALL = (1 << 5) - 1
} eAttrDomainMask;
ENUM_OPERATORS(eAttrDomainMask, ATTR_DOMAIN_MASK_ALL);
#define ATTR_DOMAIN_AS_MASK(domain) ((eAttrDomainMask)((1 << (int)(domain))))

View File

@ -17,17 +17,17 @@ extern "C" {
*/
/* Blender major and minor version. */
#define BLENDER_VERSION 305
#define BLENDER_VERSION 306
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
#define BLENDER_VERSION_CYCLE beta
#define BLENDER_VERSION_CYCLE alpha
/* TODO proper version bump. */
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 10
#define BLENDER_FILE_SUBVERSION 0
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -108,7 +108,7 @@ BVHTree *bvhtree_from_editmesh_verts(
*/
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
struct BMEditMesh *em,
const blender::BitVector<> &mask,
blender::BitSpan mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -124,7 +124,7 @@ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
BVHTree *bvhtree_from_mesh_verts_ex(struct BVHTreeFromMesh *data,
const float (*vert_positions)[3],
int verts_num,
const blender::BitVector<> &verts_mask,
blender::BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -138,7 +138,7 @@ BVHTree *bvhtree_from_editmesh_edges(
*/
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
struct BMEditMesh *em,
const blender::BitVector<> &edges_mask,
blender::BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -156,7 +156,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(struct BVHTreeFromMesh *data,
const float (*vert_positions)[3],
const struct MEdge *edge,
int edges_num,
const blender::BitVector<> &edges_mask,
blender::BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -170,7 +170,7 @@ BVHTree *bvhtree_from_editmesh_looptri(
*/
BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
struct BMEditMesh *em,
const blender::BitVector<> &mask,
blender::BitSpan mask,
int looptri_num_active,
float epsilon,
int tree_type,
@ -184,7 +184,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(struct BVHTreeFromMesh *data,
const struct MLoop *mloop,
const struct MLoopTri *looptri,
int looptri_num,
const blender::BitVector<> &mask,
blender::BitSpan mask,
int looptri_num_active,
float epsilon,
int tree_type,

View File

@ -282,6 +282,7 @@ bool CustomData_has_layer(const struct CustomData *data, int type);
* Returns the number of layers with this type.
*/
int CustomData_number_of_layers(const struct CustomData *data, int type);
int CustomData_number_of_anonymous_layers(const struct CustomData *data, int type);
int CustomData_number_of_layers_typemask(const struct CustomData *data, eCustomDataMask mask);
/**

View File

@ -41,10 +41,10 @@ struct PropertyRNA;
*/
void BKE_nlastrip_free(struct NlaStrip *strip, bool do_id_user);
/**
* Remove the given NLA track from the set of NLA tracks, free the track's data,
* and the track itself.
* Remove & Frees all NLA strips from the given NLA track,
* then frees (doesn't remove) the track itself.
*/
void BKE_nlatrack_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
void BKE_nlatrack_free(struct NlaTrack *nlt, bool do_id_user);
/**
* Free the elements of type NLA Tracks provided in the given list, but do not free
* the list itself since that is not free-standing
@ -95,6 +95,17 @@ struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
bool is_liboverride);
/**
* Removes the given NLA track from the list of tracks provided.
*/
void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt);
/**
* Remove the given NLA track from the list of NLA tracks, free the track's data,
* and the track itself.
*/
void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
/**
* Create a NLA Strip referencing the given Action.
*/

View File

@ -316,9 +316,6 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) cons
GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner) const
{
if (writable_ != Writable) {
return {};
}
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return {};

View File

@ -37,10 +37,6 @@ class BuiltinAttributeProvider {
Creatable,
NonCreatable,
};
enum WritableEnum {
Writable,
Readonly,
};
enum DeletableEnum {
Deletable,
NonDeletable,
@ -51,7 +47,6 @@ class BuiltinAttributeProvider {
const eAttrDomain domain_;
const eCustomDataType data_type_;
const CreatableEnum createable_;
const WritableEnum writable_;
const DeletableEnum deletable_;
const AttributeValidator validator_;
@ -60,14 +55,12 @@ class BuiltinAttributeProvider {
const eAttrDomain domain,
const eCustomDataType data_type,
const CreatableEnum createable,
const WritableEnum writable,
const DeletableEnum deletable,
AttributeValidator validator = {})
: name_(std::move(name)),
domain_(domain),
data_type_(data_type),
createable_(createable),
writable_(writable),
deletable_(deletable),
validator_(validator)
{
@ -205,20 +198,14 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
const eCustomDataType attribute_type,
const eCustomDataType stored_type,
const CreatableEnum creatable,
const WritableEnum writable,
const DeletableEnum deletable,
const CustomDataAccessInfo custom_data_access,
const AsReadAttribute as_read_attribute,
const AsWriteAttribute as_write_attribute,
const UpdateOnChange update_on_write,
const AttributeValidator validator = {})
: BuiltinAttributeProvider(std::move(attribute_name),
domain,
attribute_type,
creatable,
writable,
deletable,
validator),
: BuiltinAttributeProvider(
std::move(attribute_name), domain, attribute_type, creatable, deletable, validator),
stored_type_(stored_type),
custom_data_access_(custom_data_access),
as_read_attribute_(as_read_attribute),

View File

@ -29,6 +29,7 @@
#include "MEM_guardedalloc.h"
using blender::BitSpan;
using blender::BitVector;
using blender::float3;
using blender::IndexRange;
@ -672,7 +673,7 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active)
{
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
@ -706,7 +707,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
int axis,
const float (*positions)[3],
const int verts_num,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active)
{
if (!verts_mask.is_empty()) {
@ -737,7 +738,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -764,7 +765,7 @@ BVHTree *bvhtree_from_editmesh_verts(
BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data,
const float (*vert_positions)[3],
const int verts_num,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -794,7 +795,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active)
{
BM_mesh_elem_table_ensure(em->bm, BM_EDGE);
@ -833,7 +834,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
const MEdge *edge,
const int edge_num,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -871,7 +872,7 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -899,7 +900,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
const float (*vert_positions)[3],
const MEdge *edge,
const int edges_num,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -931,7 +932,7 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon,
const float (*positions)[3],
const MFace *face,
const int faces_num,
const BitVector<> &faces_mask,
const BitSpan faces_mask,
int faces_num_active)
{
if (faces_num == 0) {
@ -984,7 +985,7 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active)
{
const int looptri_num = em->tottri;
@ -1038,7 +1039,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon,
const MLoop *mloop,
const MLoopTri *looptri,
const int looptri_num,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active)
{
if (!looptri_mask.is_empty()) {
@ -1079,7 +1080,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon,
BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active,
float epsilon,
int tree_type,
@ -1109,7 +1110,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data,
const struct MLoop *mloop,
const struct MLoopTri *looptri,
const int looptri_num,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active,
float epsilon,
int tree_type,

View File

@ -2991,6 +2991,19 @@ int CustomData_number_of_layers(const CustomData *data, const int type)
return number;
}
int CustomData_number_of_anonymous_layers(const CustomData *data, const int type)
{
int number = 0;
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type && data->layers[i].anonymous_id != nullptr) {
number++;
}
}
return number;
}
int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask)
{
int number = 0;

View File

@ -375,7 +375,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
@ -387,7 +386,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -399,7 +397,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,
@ -411,7 +408,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -423,7 +419,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float3>,
@ -435,7 +430,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float3>,
@ -453,7 +447,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int8_t>,
@ -466,7 +459,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int8_t>,
@ -479,7 +471,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -495,7 +486,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -514,7 +504,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -533,7 +522,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -552,7 +540,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -569,7 +556,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int>,
@ -582,7 +568,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<bool>,

View File

@ -129,7 +129,7 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
public:
InstancePositionAttributeProvider()
: BuiltinAttributeProvider(
"position", ATTR_DOMAIN_INSTANCE, CD_PROP_FLOAT3, NonCreatable, Writable, NonDeletable)
"position", ATTR_DOMAIN_INSTANCE, CD_PROP_FLOAT3, NonCreatable, NonDeletable)
{
}
@ -200,7 +200,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
instance_custom_data_access,
make_array_read_attribute<int>,

View File

@ -1137,48 +1137,6 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
};
/**
* This provider makes face normals available as a read-only float3 attribute.
*/
class NormalAttributeProvider final : public BuiltinAttributeProvider {
public:
NormalAttributeProvider()
: BuiltinAttributeProvider(
"normal", ATTR_DOMAIN_FACE, CD_PROP_FLOAT3, NonCreatable, Readonly, NonDeletable)
{
}
GVArray try_get_for_read(const void *owner) const final
{
const Mesh *mesh = static_cast<const Mesh *>(owner);
if (mesh == nullptr || mesh->totpoly == 0) {
return {};
}
return VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly});
}
GAttributeWriter try_get_for_write(void * /*owner*/) const final
{
return {};
}
bool try_delete(void * /*owner*/) const final
{
return false;
}
bool try_create(void * /*owner*/, const AttributeInit & /*initializer*/) const final
{
return false;
}
bool exists(const void *owner) const final
{
const Mesh *mesh = static_cast<const Mesh *>(owner);
return mesh->totpoly != 0;
}
};
/**
* In this function all the attribute providers for a mesh component are created. Most data in this
* function is statically allocated, because it does not change over time.
@ -1222,21 +1180,17 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
make_array_write_attribute<float3>,
tag_component_positions_changed);
static NormalAttributeProvider normal;
static BuiltinCustomDataLayerProvider id("id",
ATTR_DOMAIN_POINT,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,
@ -1255,7 +1209,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
face_access,
make_array_read_attribute<int>,
@ -1269,7 +1222,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_BOOL,
CD_MPOLY,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
@ -1281,7 +1233,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
edge_access,
make_array_read_attribute<bool>,
@ -1294,7 +1245,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_FLOAT,
CD_CREASE,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
edge_access,
make_array_read_attribute<float>,
@ -1308,7 +1258,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access);
return ComponentAttributeProviders(
{&position, &id, &material_index, &shade_smooth, &sharp_edge, &normal, &crease},
{&position, &id, &material_index, &shade_smooth, &sharp_edge, &crease},
{&corner_custom_data,
&vertex_groups,
&point_custom_data,

View File

@ -142,7 +142,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
@ -153,7 +152,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -164,7 +162,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,

View File

@ -17,7 +17,6 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BLI_bit_vector.hh"
#include "BLI_bounds.hh"
#include "BLI_edgehash.h"
#include "BLI_endian_switch.h"
@ -66,7 +65,6 @@
#include "BLO_read_write.h"
using blender::BitVector;
using blender::float3;
using blender::MutableSpan;
using blender::Span;

View File

@ -316,7 +316,7 @@ void BKE_mesh_foreach_mapped_subdiv_face_center(
BKE_mesh_vertex_normals_ensure(mesh) :
nullptr;
const int *index = static_cast<const int *>(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX));
const blender::BitVector<> &facedot_tags = mesh->runtime->subsurf_face_dot_tags;
const blender::BitSpan facedot_tags = mesh->runtime->subsurf_face_dot_tags;
if (index) {
for (int i = 0; i < mesh->totpoly; i++, mp++) {

View File

@ -42,6 +42,7 @@
using blender::BitVector;
using blender::float3;
using blender::int2;
using blender::MutableBitSpan;
using blender::MutableSpan;
using blender::short2;
using blender::Span;
@ -1238,7 +1239,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const Span<MLoop> mloop
const Span<int2> edge_to_loops,
const Span<int> loop_to_poly,
const int *e2l_prev,
BitVector<> &skip_loops,
MutableBitSpan skip_loops,
const int ml_curr_index,
const int ml_prev_index,
const int mp_curr_index)

View File

@ -91,7 +91,7 @@ void BKE_nlastrip_free(NlaStrip *strip, const bool do_id_user)
MEM_freeN(strip);
}
void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
void BKE_nlatrack_free(NlaTrack *nlt, const bool do_id_user)
{
NlaStrip *strip, *stripn;
@ -107,12 +107,7 @@ void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
}
/* free NLA track itself now */
if (tracks) {
BLI_freelinkN(tracks, nlt);
}
else {
MEM_freeN(nlt);
}
MEM_freeN(nlt);
}
void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
@ -127,7 +122,7 @@ void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
/* free tracks one by one */
for (nlt = tracks->first; nlt; nlt = nltn) {
nltn = nlt->next;
BKE_nlatrack_free(tracks, nlt, do_id_user);
BKE_nlatrack_remove_and_free(tracks, nlt, do_id_user);
}
/* clear the list's pointers to be safe */
@ -514,6 +509,20 @@ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
}
}
/* Removing ------------------------------------------ */
void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt)
{
BLI_assert(tracks);
BLI_remlink(tracks, nlt);
}
void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user)
{
BKE_nlatrack_remove(tracks, nlt);
BKE_nlatrack_free(nlt, do_id_user);
}
/* *************************************************** */
/* NLA Evaluation <-> Editing Stuff */

View File

@ -91,4 +91,29 @@ TEST(nla_track, BKE_nlatrack_remove_strip)
EXPECT_EQ(-1, BLI_findindex(&track.strips, &strip2));
}
TEST(nla_track, BKE_nlatrack_remove_and_free)
{
AnimData adt{};
NlaTrack *track1;
NlaTrack *track2;
// 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.
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.
EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track2));
// 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));
}
} // namespace blender::bke::tests

View File

@ -2266,6 +2266,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
return pbvh;
}
ob->sculpt->islands_valid = false;
if (ob->sculpt->bm != nullptr) {
/* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
pbvh = build_pbvh_for_dynamic_topology(ob);

View File

@ -956,7 +956,9 @@ ScrArea *BKE_screen_area_map_find_area_xy(const ScrAreaMap *areamap,
const int xy[2])
{
LISTBASE_FOREACH (ScrArea *, area, &areamap->areabase) {
if (BLI_rcti_isect_pt_v(&area->totrct, xy)) {
/* Test area's outer screen verts, not inner 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)) {
return area;
}

View File

@ -0,0 +1,234 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*
* This file provides the basis for processing "indexed bits" (i.e. every bit has an index).
* The main purpose of this file is to define how bits are indexed within a memory buffer.
* For example, one has to define whether the first bit is the least or most significant bit and
* how endianness affect the bit order.
*
* The order is defined as follows:
* - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual.
* - Within each #BitInt, the bits are ordered from least to most significant.
*/
#include "BLI_index_range.hh"
#include "BLI_utildefines.h"
#include <ostream>
namespace blender::bits {
/** Using a large integer type is better because then it's easier to process many bits at once. */
using BitInt = uint64_t;
/** Number of bits that fit into #BitInt. */
static constexpr int64_t BitsPerInt = int64_t(sizeof(BitInt) * 8);
/** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */
static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(BitInt) >= 2) + (sizeof(BitInt) >= 4) +
(sizeof(BitInt) >= 8);
/** Bit mask containing a 1 for the last few bits that index a bit inside of an #BitInt. */
static constexpr BitInt BitIndexMask = (BitInt(1) << BitToIntIndexShift) - 1;
inline BitInt mask_first_n_bits(const int64_t n)
{
BLI_assert(n >= 0);
BLI_assert(n <= BitsPerInt);
if (n == BitsPerInt) {
return BitInt(-1);
}
return (BitInt(1) << n) - 1;
}
inline BitInt mask_last_n_bits(const int64_t n)
{
return ~mask_first_n_bits(BitsPerInt - n);
}
inline BitInt mask_range_bits(const int64_t start, const int64_t size)
{
BLI_assert(start >= 0);
BLI_assert(size >= 0);
const int64_t end = start + size;
BLI_assert(end <= BitsPerInt);
if (end == BitsPerInt) {
return mask_last_n_bits(size);
}
return ((BitInt(1) << end) - 1) & ~((BitInt(1) << start) - 1);
}
inline BitInt mask_single_bit(const int64_t bit_index)
{
BLI_assert(bit_index >= 0);
BLI_assert(bit_index < BitsPerInt);
return BitInt(1) << bit_index;
}
inline BitInt *int_containing_bit(BitInt *data, const int64_t bit_index)
{
return data + (bit_index >> BitToIntIndexShift);
}
inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_index)
{
return data + (bit_index >> BitToIntIndexShift);
}
/**
* This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but
* not changed.
*/
class BitRef {
private:
/** Points to the exact integer that the bit is in. */
const BitInt *int_;
/** All zeros except for a single one at the bit that is referenced. */
BitInt mask_;
friend class MutableBitRef;
public:
BitRef() = default;
/**
* Reference a specific bit in an array. Note that #data does *not* have to point to the
* exact integer the bit is in.
*/
BitRef(const BitInt *data, const int64_t bit_index)
{
int_ = int_containing_bit(data, bit_index);
mask_ = mask_single_bit(bit_index & BitIndexMask);
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const BitInt value = *int_;
const BitInt masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
};
/**
* Similar to #BitRef, but also allows changing the referenced bit.
*/
class MutableBitRef {
private:
/** Points to the integer that the bit is in. */
BitInt *int_;
/** All zeros except for a single one at the bit that is referenced. */
BitInt mask_;
public:
MutableBitRef() = default;
/**
* Reference a specific bit in an array. Note that #data does *not* have to point to the
* exact int the bit is in.
*/
MutableBitRef(BitInt *data, const int64_t bit_index)
{
int_ = int_containing_bit(data, bit_index);
mask_ = mask_single_bit(bit_index & BitIndexMask);
}
/**
* Support implicitly casting to a read-only #BitRef.
*/
operator BitRef() const
{
BitRef bit_ref;
bit_ref.int_ = int_;
bit_ref.mask_ = mask_;
return bit_ref;
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const BitInt value = *int_;
const BitInt masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
/**
* Change the bit to a 1.
*/
void set()
{
*int_ |= mask_;
}
/**
* Change the bit to a 0.
*/
void reset()
{
*int_ &= ~mask_;
}
/**
* Change the bit to a 1 if #value is true and 0 otherwise. If the value is highly unpredictable
* by the CPU branch predictor, it can be faster to use #set_branchless instead.
*/
void set(const bool value)
{
if (value) {
this->set();
}
else {
this->reset();
}
}
/**
* Does the same as #set, but does not use a branch. This is faster when the input value is
* unpredictable for the CPU branch predictor (best case for this function is a uniform random
* distribution with 50% probability for true and false). If the value is predictable, this is
* likely slower than #set.
*/
void set_branchless(const bool value)
{
const BitInt value_int = BitInt(value);
BLI_assert(ELEM(value_int, 0, 1));
const BitInt old = *int_;
*int_ =
/* Unset bit. */
(~mask_ & old)
/* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */
| (mask_ & ~(value_int - 1));
}
};
inline std::ostream &operator<<(std::ostream &stream, const BitRef &bit)
{
return stream << (bit ? "1" : "0");
}
inline std::ostream &operator<<(std::ostream &stream, const MutableBitRef &bit)
{
return stream << BitRef(bit);
}
} // namespace blender::bits
namespace blender {
using bits::BitRef;
using bits::MutableBitRef;
} // namespace blender

View File

@ -0,0 +1,290 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_bit_ref.hh"
#include "BLI_index_range.hh"
#include "BLI_memory_utils.hh"
namespace blender::bits {
/** Base class for a const and non-const bit-iterator. */
class BitIteratorBase {
protected:
const BitInt *data_;
int64_t bit_index_;
public:
BitIteratorBase(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index)
{
}
BitIteratorBase &operator++()
{
bit_index_++;
return *this;
}
friend bool operator!=(const BitIteratorBase &a, const BitIteratorBase &b)
{
BLI_assert(a.data_ == b.data_);
return a.bit_index_ != b.bit_index_;
}
};
/** Allows iterating over the bits in a memory buffer. */
class BitIterator : public BitIteratorBase {
public:
BitIterator(const BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index)
{
}
BitRef operator*() const
{
return BitRef(data_, bit_index_);
}
};
/** Allows iterating over the bits in a memory buffer. */
class MutableBitIterator : public BitIteratorBase {
public:
MutableBitIterator(BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index)
{
}
MutableBitRef operator*() const
{
return MutableBitRef(const_cast<BitInt *>(data_), bit_index_);
}
};
/**
* Similar to #Span, but references a range of bits instead of normal C++ types (which must be at
* least one byte large). Use #MutableBitSpan if the values are supposed to be modified.
*
* The beginning and end of a #BitSpan does *not* have to be at byte/int boundaries. It can start
* and end at any bit.
*/
class BitSpan {
private:
/** Base pointer to the integers containing the bits. The actual bit span might start at a much
* higher address when `bit_range_.start()` is large. */
const BitInt *data_ = nullptr;
/** The range of referenced bits. */
IndexRange bit_range_ = {0, 0};
public:
/** Construct an empty span. */
BitSpan() = default;
BitSpan(const BitInt *data, const int64_t size_in_bits) : data_(data), bit_range_(size_in_bits)
{
}
BitSpan(const BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range)
{
}
/** Number of bits referenced by the span. */
int64_t size() const
{
return bit_range_.size();
}
bool is_empty() const
{
return bit_range_.is_empty();
}
IndexRange index_range() const
{
return IndexRange(bit_range_.size());
}
BitRef operator[](const int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < bit_range_.size());
return {data_, bit_range_.start() + index};
}
BitSpan slice(const IndexRange range) const
{
return {data_, bit_range_.slice(range)};
}
const BitInt *data() const
{
return data_;
}
const IndexRange &bit_range() const
{
return bit_range_;
}
BitIterator begin() const
{
return {data_, bit_range_.start()};
}
BitIterator end() const
{
return {data_, bit_range_.one_after_last()};
}
};
/** Same as #BitSpan, but also allows modifying the referenced bits. */
class MutableBitSpan {
private:
BitInt *data_ = nullptr;
IndexRange bit_range_ = {0, 0};
public:
MutableBitSpan() = default;
MutableBitSpan(BitInt *data, const int64_t size) : data_(data), bit_range_(size)
{
}
MutableBitSpan(BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range)
{
}
int64_t size() const
{
return bit_range_.size();
}
bool is_empty() const
{
return bit_range_.is_empty();
}
IndexRange index_range() const
{
return IndexRange(bit_range_.size());
}
MutableBitRef operator[](const int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < bit_range_.size());
return {data_, bit_range_.start() + index};
}
MutableBitSpan slice(const IndexRange range) const
{
return {data_, bit_range_.slice(range)};
}
BitInt *data() const
{
return data_;
}
const IndexRange &bit_range() const
{
return bit_range_;
}
MutableBitIterator begin() const
{
return {data_, bit_range_.start()};
}
MutableBitIterator end() const
{
return {data_, bit_range_.one_after_last()};
}
operator BitSpan() const
{
return {data_, bit_range_};
}
/** Sets all referenced bits to 1. */
void set_all()
{
const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt);
{
BitInt &first_int = *int_containing_bit(data_, bit_range_.start());
const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask,
ranges.prefix.size());
first_int |= first_int_mask;
}
{
BitInt *start = int_containing_bit(data_, ranges.aligned.start());
const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt;
constexpr BitInt fill_value = BitInt(-1);
initialized_fill_n(start, ints_to_fill, fill_value);
}
{
BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1);
const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size());
last_int |= last_int_mask;
}
}
/** Sets all referenced bits to 0. */
void reset_all()
{
const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt);
{
BitInt &first_int = *int_containing_bit(data_, bit_range_.start());
const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask,
ranges.prefix.size());
first_int &= ~first_int_mask;
}
{
BitInt *start = int_containing_bit(data_, ranges.aligned.start());
const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt;
constexpr BitInt fill_value = 0;
initialized_fill_n(start, ints_to_fill, fill_value);
}
{
BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1);
const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size());
last_int &= ~last_int_mask;
}
}
/** Sets all referenced bits to either 0 or 1. */
void set_all(const bool value)
{
if (value) {
this->set_all();
}
else {
this->reset_all();
}
}
/** Same as #set_all to mirror #MutableSpan. */
void fill(const bool value)
{
this->set_all(value);
}
};
inline std::ostream &operator<<(std::ostream &stream, const BitSpan &span)
{
stream << "(Size: " << span.size() << ", ";
for (const BitRef bit : span) {
stream << bit;
}
stream << ")";
return stream;
}
inline std::ostream &operator<<(std::ostream &stream, const MutableBitSpan &span)
{
return stream << BitSpan(span);
}
} // namespace blender::bits
namespace blender {
using bits::BitSpan;
using bits::MutableBitSpan;
} // namespace blender

View File

@ -38,142 +38,11 @@
#include <cstring>
#include "BLI_allocator.hh"
#include "BLI_index_range.hh"
#include "BLI_memory_utils.hh"
#include "BLI_bit_span.hh"
#include "BLI_span.hh"
namespace blender::bits {
/**
* Using a large integer type is better because then it's easier to process many bits at once.
*/
using IntType = uint64_t;
static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8);
static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) +
(sizeof(IntType) >= 8);
static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1;
/**
* This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but
* not changed.
*/
class BitRef {
private:
/** Points to the integer that the bit is in. */
const IntType *ptr_;
/** All zeros except for a single one at the bit that is referenced. */
IntType mask_;
friend class MutableBitRef;
public:
BitRef() = default;
/**
* Reference a specific bit in an array. Note that #ptr does *not* have to point to the
* exact integer the bit is in.
*/
BitRef(const IntType *ptr, const int64_t bit_index)
{
ptr_ = ptr + (bit_index >> BitToIntIndexShift);
mask_ = IntType(1) << (bit_index & BitIndexMask);
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const IntType value = *ptr_;
const IntType masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
};
/**
* Similar to #BitRef, but also allows changing the referenced bit.
*/
class MutableBitRef {
private:
/** Points to the integer that the bit is in. */
IntType *ptr_;
/** All zeros except for a single one at the bit that is referenced. */
IntType mask_;
public:
MutableBitRef() = default;
/**
* Reference a specific bit in an array. Note that #ptr does *not* have to point to the
* exact int the bit is in.
*/
MutableBitRef(IntType *ptr, const int64_t bit_index)
{
ptr_ = ptr + (bit_index >> BitToIntIndexShift);
mask_ = IntType(1) << IntType(bit_index & BitIndexMask);
}
/**
* Support implicitly casting to a read-only #BitRef.
*/
operator BitRef() const
{
BitRef bit_ref;
bit_ref.ptr_ = ptr_;
bit_ref.mask_ = mask_;
return bit_ref;
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const IntType value = *ptr_;
const IntType masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
/**
* Change the bit to a 1.
*/
void set()
{
*ptr_ |= mask_;
}
/**
* Change the bit to a 0.
*/
void reset()
{
*ptr_ &= ~mask_;
}
/**
* Change the bit to a 1 if #value is true and 0 otherwise.
*/
void set(const bool value)
{
if (value) {
this->set();
}
else {
this->reset();
}
}
};
template<
/**
* Number of bits that can be stored in the vector without doing an allocation.
@ -193,13 +62,13 @@ class BitVector {
static constexpr int64_t IntsInInlineBuffer = required_ints_for_bits(InlineBufferCapacity);
static constexpr int64_t BitsInInlineBuffer = IntsInInlineBuffer * BitsPerInt;
static constexpr int64_t AllocationAlignment = alignof(IntType);
static constexpr int64_t AllocationAlignment = alignof(BitInt);
/**
* Points to the first integer used by the vector. It might point to the memory in the inline
* buffer.
*/
IntType *data_;
BitInt *data_;
/** Current size of the vector in bits. */
int64_t size_in_bits_;
@ -211,7 +80,7 @@ class BitVector {
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
/** Contains the bits as long as the vector is small enough. */
BLI_NO_UNIQUE_ADDRESS TypedBuffer<IntType, IntsInInlineBuffer> inline_buffer_;
BLI_NO_UNIQUE_ADDRESS TypedBuffer<BitInt, IntsInInlineBuffer> inline_buffer_;
public:
BitVector(Allocator allocator = {}) noexcept : allocator_(allocator)
@ -219,7 +88,7 @@ class BitVector {
data_ = inline_buffer_;
size_in_bits_ = 0;
capacity_in_bits_ = BitsInInlineBuffer;
uninitialized_fill_n(data_, IntsInInlineBuffer, IntType(0));
uninitialized_fill_n(data_, IntsInInlineBuffer, BitInt(0));
}
BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator)
@ -236,8 +105,8 @@ class BitVector {
}
else {
/* Allocate a new array because the inline buffer is too small. */
data_ = static_cast<IntType *>(
allocator_.allocate(ints_to_copy * sizeof(IntType), AllocationAlignment, __func__));
data_ = static_cast<BitInt *>(
allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__));
capacity_in_bits_ = ints_to_copy * BitsPerInt;
}
size_in_bits_ = other.size_in_bits_;
@ -303,6 +172,16 @@ class BitVector {
return move_assign_container(*this, std::move(other));
}
operator BitSpan() const
{
return {data_, IndexRange(size_in_bits_)};
}
operator MutableBitSpan()
{
return {data_, IndexRange(size_in_bits_)};
}
/**
* Number of bits in the bit vector.
*/
@ -352,80 +231,24 @@ class BitVector {
size_in_bits_++;
}
class Iterator {
private:
const BitVector *vector_;
int64_t index_;
public:
Iterator(const BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
{
}
Iterator &operator++()
{
index_++;
return *this;
}
friend bool operator!=(const Iterator &a, const Iterator &b)
{
BLI_assert(a.vector_ == b.vector_);
return a.index_ != b.index_;
}
BitRef operator*() const
{
return (*vector_)[index_];
}
};
class MutableIterator {
private:
BitVector *vector_;
int64_t index_;
public:
MutableIterator(BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
{
}
MutableIterator &operator++()
{
index_++;
return *this;
}
friend bool operator!=(const MutableIterator &a, const MutableIterator &b)
{
BLI_assert(a.vector_ == b.vector_);
return a.index_ != b.index_;
}
MutableBitRef operator*() const
{
return (*vector_)[index_];
}
};
Iterator begin() const
BitIterator begin() const
{
return {*this, 0};
return {data_, 0};
}
Iterator end() const
BitIterator end() const
{
return {*this, size_in_bits_};
return {data_, size_in_bits_};
}
MutableIterator begin()
MutableBitIterator begin()
{
return {*this, 0};
return {data_, 0};
}
MutableIterator end()
MutableBitIterator end()
{
return {*this, size_in_bits_};
return {data_, size_in_bits_};
}
/**
@ -441,31 +264,8 @@ class BitVector {
}
size_in_bits_ = new_size_in_bits;
if (old_size_in_bits < new_size_in_bits) {
this->fill_range(IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits), value);
}
}
/**
* Set #value for every element in #range.
*/
void fill_range(const IndexRange range, const bool value)
{
const AlignedIndexRanges aligned_ranges = split_index_range_by_alignment(range, BitsPerInt);
/* Fill first few bits. */
for (const int64_t i : aligned_ranges.prefix) {
(*this)[i].set(value);
}
/* Fill entire ints at once. */
const int64_t start_fill_int_index = aligned_ranges.aligned.start() / BitsPerInt;
const int64_t ints_to_fill = aligned_ranges.aligned.size() / BitsPerInt;
const IntType fill_value = value ? IntType(-1) : IntType(0);
initialized_fill_n(data_ + start_fill_int_index, ints_to_fill, fill_value);
/* Fill bits in the end that don't cover a full int. */
for (const int64_t i : aligned_ranges.suffix) {
(*this)[i].set(value);
MutableBitSpan(data_, IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits))
.set_all(value);
}
}
@ -474,7 +274,7 @@ class BitVector {
*/
void fill(const bool value)
{
this->fill_range(IndexRange(0, size_in_bits_), value);
MutableBitSpan(data_, size_in_bits_).set_all(value);
}
/**
@ -517,7 +317,7 @@ class BitVector {
}
BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits,
const IntType initial_value_for_new_ints = 0x00)
const BitInt initial_value_for_new_ints = 0)
{
if (capacity_in_bits_ >= min_capacity_in_bits) {
return;
@ -531,8 +331,8 @@ class BitVector {
const int64_t new_capacity_in_ints = std::max(min_capacity_in_ints, min_new_capacity_in_ints);
const int64_t ints_to_copy = this->used_ints_amount();
IntType *new_data = static_cast<IntType *>(allocator_.allocate(
new_capacity_in_ints * sizeof(IntType), AllocationAlignment, __func__));
BitInt *new_data = static_cast<BitInt *>(
allocator_.allocate(new_capacity_in_ints * sizeof(BitInt), AllocationAlignment, __func__));
uninitialized_copy_n(data_, ints_to_copy, new_data);
/* Always initialize new capacity even if it isn't used yet. That's necessary to avoid warnings
* caused by using uninitialized memory. This happens when e.g. setting a clearing a bit in an
@ -562,7 +362,5 @@ class BitVector {
} // namespace blender::bits
namespace blender {
using bits::BitRef;
using bits::BitVector;
using bits::MutableBitRef;
} // namespace blender

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

@ -457,6 +457,8 @@ if(WITH_GTESTS)
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc
tests/BLI_bit_ref_test.cc
tests/BLI_bit_span_test.cc
tests/BLI_bit_vector_test.cc
tests/BLI_bitmap_test.cc
tests/BLI_bounds_test.cc

View File

@ -1490,15 +1490,18 @@ void rotate_eul(float beul[3], const char axis, const float angle)
void compatible_eul(float eul[3], const float oldrot[3])
{
/* we could use M_PI as pi_thresh: which is correct but 5.1 gives better results.
* Checked with baking actions to fcurves - campbell */
const float pi_thresh = (5.1f);
/* When the rotation exceeds 180 degrees, it can be wrapped by 360 degrees
* 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) {
@ -1511,29 +1514,17 @@ void compatible_eul(float eul[3], const float oldrot[3])
}
}
/* is 1 of the axis rotations larger than 180 degrees and the other small? NO ELSE IF!! */
if (fabsf(deul[0]) > 3.2f && fabsf(deul[1]) < 1.6f && fabsf(deul[2]) < 1.6f) {
if (deul[0] > 0.0f) {
eul[0] -= pi_x2;
}
else {
eul[0] += pi_x2;
}
}
if (fabsf(deul[1]) > 3.2f && fabsf(deul[2]) < 1.6f && fabsf(deul[0]) < 1.6f) {
if (deul[1] > 0.0f) {
eul[1] -= pi_x2;
}
else {
eul[1] += pi_x2;
}
}
if (fabsf(deul[2]) > 3.2f && fabsf(deul[0]) < 1.6f && fabsf(deul[1]) < 1.6f) {
if (deul[2] > 0.0f) {
eul[2] -= pi_x2;
}
else {
eul[2] += pi_x2;
uint j = 1, k = 2;
for (i = 0; i < 3; j = k, k = i++) {
/* 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;
}
else {
eul[i] += pi_x2;
}
}
}
}

View File

@ -0,0 +1,160 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include <array>
#include "BLI_bit_ref.hh"
#include "testing/testing.h"
namespace blender::bits::tests {
TEST(bit_ref, MaskFirstNBits)
{
EXPECT_EQ(mask_first_n_bits(0), 0);
EXPECT_EQ(mask_first_n_bits(1),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001);
EXPECT_EQ(mask_first_n_bits(5),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111);
EXPECT_EQ(mask_first_n_bits(63),
0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
EXPECT_EQ(mask_first_n_bits(64),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
}
TEST(bit_ref, MaskLastNBits)
{
EXPECT_EQ(mask_last_n_bits(0),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_last_n_bits(1),
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_last_n_bits(5),
0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_last_n_bits(63),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1110);
EXPECT_EQ(mask_last_n_bits(64),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
}
TEST(bit_ref, MaskSingleBit)
{
EXPECT_EQ(mask_single_bit(0), 1);
EXPECT_EQ(mask_single_bit(1),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010);
EXPECT_EQ(mask_single_bit(5),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
EXPECT_EQ(mask_single_bit(63),
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
}
TEST(bit_ref, IntContainingBit)
{
std::array<uint64_t, 5> array;
uint64_t *data = array.data();
EXPECT_EQ(int_containing_bit(data, 0), data);
EXPECT_EQ(int_containing_bit(data, 1), data);
EXPECT_EQ(int_containing_bit(data, 63), data);
EXPECT_EQ(int_containing_bit(data, 64), data + 1);
EXPECT_EQ(int_containing_bit(data, 65), data + 1);
EXPECT_EQ(int_containing_bit(data, 100), data + 1);
EXPECT_EQ(int_containing_bit(data, 127), data + 1);
EXPECT_EQ(int_containing_bit(data, 128), data + 2);
const uint64_t *data_const = data;
EXPECT_EQ(int_containing_bit(data_const, 0), data_const);
EXPECT_EQ(int_containing_bit(data_const, 1), data_const);
EXPECT_EQ(int_containing_bit(data_const, 63), data_const);
EXPECT_EQ(int_containing_bit(data_const, 64), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 65), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 100), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 127), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 128), data_const + 2);
}
TEST(bit_ref, Test)
{
uint64_t data = (1 << 3) | (1 << 7);
EXPECT_FALSE(BitRef(&data, 0).test());
EXPECT_FALSE(BitRef(&data, 1).test());
EXPECT_FALSE(BitRef(&data, 2).test());
EXPECT_TRUE(BitRef(&data, 3).test());
EXPECT_FALSE(BitRef(&data, 4));
EXPECT_FALSE(BitRef(&data, 5));
EXPECT_FALSE(BitRef(&data, 6));
EXPECT_TRUE(BitRef(&data, 7));
EXPECT_FALSE(MutableBitRef(&data, 0).test());
EXPECT_FALSE(MutableBitRef(&data, 1).test());
EXPECT_FALSE(MutableBitRef(&data, 2).test());
EXPECT_TRUE(MutableBitRef(&data, 3).test());
EXPECT_FALSE(MutableBitRef(&data, 4));
EXPECT_FALSE(MutableBitRef(&data, 5));
EXPECT_FALSE(MutableBitRef(&data, 6));
EXPECT_TRUE(MutableBitRef(&data, 7));
}
TEST(bit_ref, Set)
{
uint64_t data = 0;
MutableBitRef(&data, 0).set();
MutableBitRef(&data, 1).set();
MutableBitRef(&data, 1).set();
MutableBitRef(&data, 4).set();
EXPECT_EQ(data, (1 << 0) | (1 << 1) | (1 << 4));
MutableBitRef(&data, 5).set(true);
MutableBitRef(&data, 1).set(false);
EXPECT_EQ(data, (1 << 0) | (1 << 4) | (1 << 5));
}
TEST(bit_ref, Reset)
{
uint64_t data = -1;
MutableBitRef(&data, 0).reset();
MutableBitRef(&data, 2).reset();
EXPECT_EQ(data, uint64_t(-1) & ~(1 << 0) & ~(1 << 2));
}
TEST(bit_ref, SetBranchless)
{
uint64_t data = 0;
MutableBitRef(&data, 0).set_branchless(true);
EXPECT_EQ(data, 1);
MutableBitRef(&data, 0).set_branchless(false);
EXPECT_EQ(data, 0);
MutableBitRef(&data, 3).set_branchless(false);
MutableBitRef(&data, 4).set_branchless(true);
EXPECT_EQ(data, 16);
MutableBitRef(&data, 3).set_branchless(true);
MutableBitRef(&data, 4).set_branchless(true);
EXPECT_EQ(data, 24);
}
TEST(bit_ref, Cast)
{
uint64_t data = 0;
MutableBitRef mutable_ref(&data, 3);
BitRef ref = mutable_ref;
EXPECT_FALSE(ref);
mutable_ref.set();
EXPECT_TRUE(ref);
}
TEST(bit_ref, MaskRangeBits)
{
EXPECT_EQ(mask_range_bits(0, 0),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(0, 1),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001);
EXPECT_EQ(mask_range_bits(0, 5),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111);
EXPECT_EQ(mask_range_bits(64, 0),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(63, 1),
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(59, 5),
0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(8, 3),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000);
EXPECT_EQ(mask_range_bits(0, 64),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
}
} // namespace blender::bits::tests

View File

@ -0,0 +1,139 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include <array>
#include "BLI_bit_span.hh"
#include "testing/testing.h"
namespace blender::bits::tests {
TEST(bit_span, DefaultConstructor)
{
{
char buffer[sizeof(BitSpan)];
memset(buffer, 0xff, sizeof(BitSpan));
BitSpan &span = *new (buffer) BitSpan();
EXPECT_TRUE(span.is_empty());
EXPECT_EQ(span.size(), 0);
}
{
char buffer[sizeof(MutableBitSpan)];
memset(buffer, 0xff, sizeof(MutableBitSpan));
MutableBitSpan &span = *new (buffer) MutableBitSpan();
EXPECT_TRUE(span.is_empty());
EXPECT_EQ(span.size(), 0);
}
}
TEST(bit_span, Iteration)
{
uint64_t data = (1 << 2) | (1 << 3);
const BitSpan span(&data, 30);
EXPECT_EQ(span.size(), 30);
int index = 0;
for (const BitRef bit : span) {
EXPECT_EQ(bit.test(), ELEM(index, 2, 3));
index++;
}
}
TEST(bit_span, MutableIteration)
{
uint64_t data = 0;
MutableBitSpan span(&data, 40);
EXPECT_EQ(span.size(), 40);
int index = 0;
for (MutableBitRef bit : span) {
bit.set(index % 4 == 0);
index++;
}
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0001'0001'0001'0001'0001'0001'0001'0001'0001'0001);
}
TEST(bit_span, SubscriptOperator)
{
uint64_t data[2] = {0, 0};
MutableBitSpan mutable_span(data, 128);
BitSpan span = mutable_span;
EXPECT_EQ(mutable_span.data(), data);
EXPECT_EQ(mutable_span.bit_range(), IndexRange(128));
EXPECT_EQ(span.data(), data);
EXPECT_EQ(span.bit_range(), IndexRange(128));
EXPECT_FALSE(mutable_span[5].test());
EXPECT_FALSE(span[5].test());
mutable_span[5].set(5);
EXPECT_TRUE(mutable_span[5].test());
EXPECT_TRUE(span[5].test());
EXPECT_FALSE(mutable_span[120].test());
EXPECT_FALSE(span[120].test());
mutable_span[120].set(120);
EXPECT_TRUE(mutable_span[120].test());
EXPECT_TRUE(span[120].test());
EXPECT_EQ(data[0],
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
EXPECT_EQ(data[1],
0b0000'0001'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
}
TEST(bit_span, RangeConstructor)
{
uint64_t data = 0;
MutableBitSpan mutable_span(&data, IndexRange(4, 3));
BitSpan span = mutable_span;
EXPECT_FALSE(mutable_span[1].test());
EXPECT_FALSE(span[1].test());
mutable_span[0].set(true);
mutable_span[1].set(true);
mutable_span[2].set(true);
mutable_span[0].set(false);
mutable_span[2].set(false);
EXPECT_TRUE(mutable_span[1].test());
EXPECT_TRUE(span[1].test());
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
}
TEST(bit_span, Set)
{
uint64_t data = 0;
MutableBitSpan(&data, 64).set_all(true);
EXPECT_EQ(data, uint64_t(-1));
MutableBitSpan(&data, 64).set_all(false);
EXPECT_EQ(data, uint64_t(0));
MutableBitSpan(&data, IndexRange(4, 8)).set_all(true);
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'1111'0000);
MutableBitSpan(&data, IndexRange(8, 30)).set_all(false);
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'0000);
}
TEST(bit_span, SetSliced)
{
std::array<uint64_t, 10> data;
memset(data.data(), 0, sizeof(data));
MutableBitSpan span{data.data(), 640};
span.slice(IndexRange(5, 500)).set_all(true);
for (const int64_t i : IndexRange(640)) {
EXPECT_EQ(span[i], i >= 5 && i < 505);
}
span.slice(IndexRange(10, 190)).set_all(false);
for (const int64_t i : IndexRange(640)) {
EXPECT_EQ(span[i], (i >= 5 && i < 10) || (i >= 200 && i < 505));
}
}
} // namespace blender::bits::tests

View File

@ -6,7 +6,7 @@
#include "testing/testing.h"
namespace blender::tests {
namespace blender::bits::tests {
TEST(bit_vector, DefaultConstructor)
{
@ -183,4 +183,4 @@ TEST(bit_vector, AppendMany)
EXPECT_TRUE(vec[5]);
}
} // namespace blender::tests
} // namespace blender::bits::tests

View File

@ -290,6 +290,24 @@ TEST(index_range, SplitByAlignment)
EXPECT_EQ(ranges.aligned, IndexRange());
EXPECT_EQ(ranges.suffix, IndexRange());
}
{
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64), 64);
EXPECT_EQ(ranges.prefix, IndexRange());
EXPECT_EQ(ranges.aligned, IndexRange(64));
EXPECT_EQ(ranges.suffix, IndexRange());
}
{
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64, 64), 64);
EXPECT_EQ(ranges.prefix, IndexRange());
EXPECT_EQ(ranges.aligned, IndexRange(64, 64));
EXPECT_EQ(ranges.suffix, IndexRange());
}
{
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(4, 8), 64);
EXPECT_EQ(ranges.prefix, IndexRange(4, 8));
EXPECT_EQ(ranges.aligned, IndexRange());
EXPECT_EQ(ranges.suffix, IndexRange());
}
}
} // namespace blender::tests

View File

@ -211,7 +211,7 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
int BM_iter_mesh_bitmap_from_filter(const char itype,
BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMElem *, void *user_data),
void *user_data)
{
@ -234,7 +234,7 @@ int BM_iter_mesh_bitmap_from_filter(const char itype,
}
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMFace *, void *user_data),
void *user_data)
{

View File

@ -21,7 +21,7 @@
#include "BLI_mempool.h"
#ifdef __cplusplus
# include "BLI_bit_vector.hh"
# include "BLI_bit_span.hh"
#endif
#ifdef __cplusplus
@ -228,14 +228,14 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
int BM_iter_mesh_bitmap_from_filter(char itype,
BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMElem *, void *user_data),
void *user_data);
/**
* Needed when we want to check faces, but return a loop aligned array.
*/
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMFace *, void *user_data),
void *user_data);

View File

@ -557,7 +557,7 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
if (mr->use_subsurf_fdots) {
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -46,7 +46,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
if (mr->use_subsurf_fdots) {
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -102,7 +102,7 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
const BitVector<> &optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges;
const BitSpan optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -76,7 +76,7 @@ static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
zero_v3(co);
const MLoop *mloop = mr->mloop;
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {

View File

@ -74,7 +74,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_FdotUV_Data *data = static_cast<MeshExtract_FdotUV_Data *>(_data);
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -77,11 +77,7 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool
if (include_generated) {
const EnumPropertyItem generated_items[] = {
{ASSET_LIBRARY_ALL,
"ALL",
ICON_BLANK1,
"All",
"Show assets from all of the listed asset libraries"},
{ASSET_LIBRARY_ALL, "ALL", 0, "All", "Show assets from all of the listed asset libraries"},
RNA_ENUM_ITEM_SEPR,
{ASSET_LIBRARY_LOCAL,
"LOCAL",
@ -90,7 +86,7 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool
"Show the assets currently available in this Blender session"},
{ASSET_LIBRARY_ESSENTIALS,
"ESSENTIALS",
ICON_BLANK1,
0,
"Essentials",
"Show the basic building blocks and utilities coming with Blender"},
{0, nullptr, 0, nullptr, nullptr},
@ -122,7 +118,7 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool
const int enum_value = ED_asset_library_reference_to_enum_value(&library_reference);
/* Use library path as description, it's a nice hint for users. */
EnumPropertyItem tmp = {
enum_value, user_library->name, ICON_BLANK1, user_library->name, user_library->path};
enum_value, user_library->name, ICON_NONE, user_library->name, user_library->path};
RNA_enum_item_add(&item, &totitem, &tmp);
}

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

@ -1008,6 +1008,60 @@ static void CURVES_OT_select_linked(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int select_more_exec(bContext *C, wmOperator * /*op*/)
{
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = curves_id->geometry.wrap();
select_adjacent(curves, false);
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
return OPERATOR_FINISHED;
}
static void CURVES_OT_select_more(wmOperatorType *ot)
{
ot->name = "Select More";
ot->idname = __func__;
ot->description = "Grow the selection by one point";
ot->exec = select_more_exec;
ot->poll = editable_curves_point_domain_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int select_less_exec(bContext *C, wmOperator * /*op*/)
{
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = curves_id->geometry.wrap();
select_adjacent(curves, true);
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
return OPERATOR_FINISHED;
}
static void CURVES_OT_select_less(wmOperatorType *ot)
{
ot->name = "Select Less";
ot->idname = __func__;
ot->description = "Shrink the selection by one point";
ot->exec = select_less_exec;
ot->poll = editable_curves_point_domain_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
namespace surface_set {
static bool surface_set_poll(bContext *C)
@ -1133,6 +1187,8 @@ void ED_operatortypes_curves()
WM_operatortype_append(CURVES_OT_select_random);
WM_operatortype_append(CURVES_OT_select_end);
WM_operatortype_append(CURVES_OT_select_linked);
WM_operatortype_append(CURVES_OT_select_more);
WM_operatortype_append(CURVES_OT_select_less);
WM_operatortype_append(CURVES_OT_surface_set);
WM_operatortype_append(CURVES_OT_delete);
}

View File

@ -267,6 +267,85 @@ void select_linked(bke::CurvesGeometry &curves)
selection.finish();
}
void select_adjacent(bke::CurvesGeometry &curves, const bool deselect)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL);
const VArray<bool> cyclic = curves.cyclic();
if (deselect) {
invert_selection(selection.span);
}
if (selection.span.type().is<bool>()) {
MutableSpan<bool> selection_typed = selection.span.typed<bool>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
/* Handle all cases in the forward direction. */
for (int point_i = points.first(); point_i < points.last(); point_i++) {
if (!selection_typed[point_i] && selection_typed[point_i + 1]) {
selection_typed[point_i] = true;
}
}
/* Handle all cases in the backwards direction. */
for (int point_i = points.last(); point_i > points.first(); point_i--) {
if (!selection_typed[point_i] && selection_typed[point_i - 1]) {
selection_typed[point_i] = true;
}
}
/* Handle cyclic curve case. */
if (cyclic[curve_i]) {
if (selection_typed[points.first()] != selection_typed[points.last()]) {
selection_typed[points.first()] = true;
selection_typed[points.last()] = true;
}
}
}
});
}
else if (selection.span.type().is<float>()) {
MutableSpan<float> selection_typed = selection.span.typed<float>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
/* Handle all cases in the forward direction. */
for (int point_i = points.first(); point_i < points.last(); point_i++) {
if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i + 1] > 0.0f)) {
selection_typed[point_i] = 1.0f;
}
}
/* Handle all cases in the backwards direction. */
for (int point_i = points.last(); point_i > points.first(); point_i--) {
if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i - 1] > 0.0f)) {
selection_typed[point_i] = 1.0f;
}
}
/* Handle cyclic curve case. */
if (cyclic[curve_i]) {
if (selection_typed[points.first()] != selection_typed[points.last()]) {
selection_typed[points.first()] = 1.0f;
selection_typed[points.last()] = 1.0f;
}
}
}
});
}
if (deselect) {
invert_selection(selection.span);
}
selection.finish();
}
void select_random(bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
uint32_t random_seed,

View File

@ -135,6 +135,11 @@ void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points);
*/
void select_linked(bke::CurvesGeometry &curves);
/**
* (De)select all the adjacent points of the current selected points.
*/
void select_adjacent(bke::CurvesGeometry &curves, bool deselect);
/**
* Select random points or curves.
*

View File

@ -239,6 +239,17 @@ enum {
UI_BUT_OVERRIDDEN = 1u << 31u,
};
/** #uiBut.dragflag */
enum {
/** By default only the left part of a button triggers dragging. A questionable design to make
* the icon but not other parts of the button draggable. Set this flag so the entire button can
* be dragged. */
UI_BUT_DRAG_FULL_BUT = (1 << 0),
/* --- Internal flags. --- */
UI_BUT_DRAGPOIN_FREE = (1 << 1),
};
/* Default font size for normal text. */
#define UI_DEFAULT_TEXT_POINTS 11.0f
@ -881,6 +892,9 @@ bool UI_but_flag_is_set(uiBut *but, int flag);
void UI_but_drawflag_enable(uiBut *but, int flag);
void UI_but_drawflag_disable(uiBut *but, int flag);
void UI_but_dragflag_enable(uiBut *but, int flag);
void UI_but_dragflag_disable(uiBut *but, int flag);
void UI_but_disable(uiBut *but, const char *disabled_hint);
void UI_but_type_set_menu_from_pulldown(uiBut *but);
@ -1789,9 +1803,12 @@ void UI_but_drag_set_id(uiBut *but, struct ID *id);
/**
* Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`).
* Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image.
*
* Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged.
*/
void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale);
/**
* Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged.
* \param asset: May be passed from a temporary variable, drag data only stores a copy of this.
*/
void UI_but_drag_set_asset(uiBut *but,
@ -1808,6 +1825,8 @@ void UI_but_drag_set_name(uiBut *but, const char *name);
* Value from button itself.
*/
void UI_but_drag_set_value(uiBut *but);
/** Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged. */
void UI_but_drag_set_image(
uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, bool use_free);

View File

@ -4224,6 +4224,10 @@ static uiBut *ui_def_but(uiBlock *block,
but->flag |= UI_BUT_UNDO;
}
if (ELEM(but->type, UI_BTYPE_COLOR)) {
but->dragflag |= UI_BUT_DRAG_FULL_BUT;
}
BLI_addtail(&block->buttons, but);
if (block->curlayout) {
@ -4287,6 +4291,7 @@ static void ui_def_but_rna__menu(bContext * /*C*/, uiLayout *layout, void *but_p
int totitems = 0;
int categories = 0;
int entries_nosepr_count = 0;
bool has_item_with_icon = false;
for (const EnumPropertyItem *item = item_array; item->identifier; item++, totitems++) {
if (!item->identifier[0]) {
/* inconsistent, but menus with categories do not look good flipped */
@ -4298,6 +4303,9 @@ static void ui_def_but_rna__menu(bContext * /*C*/, uiLayout *layout, void *but_p
/* We do not want simple separators in `entries_nosepr_count`. */
continue;
}
if (item->icon) {
has_item_with_icon = true;
}
entries_nosepr_count++;
}
@ -4402,11 +4410,18 @@ static void ui_def_but_rna__menu(bContext * /*C*/, uiLayout *layout, void *but_p
uiItemS(column);
}
else {
if (item->icon) {
int icon = item->icon;
/* Use blank icon if there is none for this item (but for some other one) to make sure labels
* align. */
if (icon == ICON_NONE && has_item_with_icon) {
icon = ICON_BLANK1;
}
if (icon) {
uiDefIconTextButI(block,
UI_BTYPE_BUT_MENU,
B_NOP,
item->icon,
icon,
item->name,
0,
0,
@ -4585,15 +4600,7 @@ static uiBut *ui_def_but_rna(uiBlock *block,
#endif
}
/* #ICON_BLANK1 can be used to add padding of the size of an icon. This is fine to align
* multiple items within a menu, but not for the menu button that only shows the label then.
*/
if ((type == UI_BTYPE_MENU) && (item[i].icon == ICON_BLANK1)) {
icon = ICON_NONE;
}
else {
icon = item[i].icon;
}
icon = item[i].icon;
}
else {
if (!str) {
@ -5887,6 +5894,16 @@ void UI_but_drawflag_disable(uiBut *but, int flag)
but->drawflag &= ~flag;
}
void UI_but_dragflag_enable(uiBut *but, int flag)
{
but->dragflag |= flag;
}
void UI_but_dragflag_disable(uiBut *but, int flag)
{
but->dragflag &= ~flag;
}
void UI_but_disable(uiBut *but, const char *disabled_hint)
{
UI_but_flag_enable(but, UI_BUT_DISABLED);

View File

@ -24,6 +24,7 @@ void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale)
{
but->imb = imb;
but->imb_scale = scale;
UI_but_dragflag_enable(but, UI_BUT_DRAG_FULL_BUT);
}
void UI_but_drag_set_asset(uiBut *but,

View File

@ -86,11 +86,6 @@ enum {
/* WARNING: rest of #uiBut.flag in UI_interface.h */
};
/** #uiBut.dragflag */
enum {
UI_BUT_DRAGPOIN_FREE = (1 << 0),
};
/** #uiBut.pie_dir */
enum RadialDirection {
UI_RADIAL_NONE = -1,

View File

@ -247,7 +247,7 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv
BLI_rcti_rctf_copy(&rect, &but->rect);
if (but->imb || but->type == UI_BTYPE_COLOR) {
if (but->dragflag & UI_BUT_DRAG_FULL_BUT) {
/* use button size itself */
}
else if (but->drawflag & UI_BUT_ICON_LEFT) {

View File

@ -92,15 +92,9 @@ void attribute_search_add_items(StringRefNull str,
StringSearch *search = BLI_string_search_new();
for (const GeometryAttributeInfo *item : infos) {
/* Don't show the legacy "normal" attribute. */
if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) {
continue;
}
if (!bke::allow_procedural_attribute_access(item->name)) {
continue;
}
BLI_string_search_add(search, item->name.c_str(), (void *)item, 0);
}

View File

@ -83,7 +83,7 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
{
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
if (ss && br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
return false;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {

View File

@ -1571,8 +1571,13 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_stroke_id_next(ob);
SCULPT_undo_push_begin(ob, op);
SCULPT_filter_cache_init(
C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
SCULPT_filter_cache_init(C,
ob,
sd,
SCULPT_UNDO_COORDS,
event->mval,
RNA_float_get(op->ptr, "area_normal_radius"),
RNA_float_get(op->ptr, "strength"));
ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob);

View File

@ -347,8 +347,13 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
SCULPT_filter_cache_init(
C, ob, sd, SCULPT_UNDO_COLOR, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
SCULPT_filter_cache_init(C,
ob,
sd,
SCULPT_UNDO_COLOR,
event->mval,
RNA_float_get(op->ptr, "area_normal_radius"),
RNA_float_get(op->ptr, "strength"));
FilterCache *filter_cache = ss->filter_cache;
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob);

View File

@ -8,7 +8,9 @@
#include "MEM_guardedalloc.h"
#include "BLI_hash.h"
#include "BLI_index_range.hh"
#include "BLI_math.h"
#include "BLI_math_vector_types.hh"
#include "BLI_task.h"
#include "DNA_meshdata_types.h"
@ -23,6 +25,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "paint_intern.h"
@ -30,14 +33,20 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "bmesh.h"
#include <cmath>
#include <cstdlib>
using blender::float2;
using blender::float3;
using blender::IndexRange;
void SCULPT_filter_to_orientation_space(float r_v[3], FilterCache *filter_cache)
{
switch (filter_cache->orientation) {
@ -96,13 +105,14 @@ void SCULPT_filter_cache_init(bContext *C,
Sculpt *sd,
const int undo_type,
const int mval[2],
float area_normal_radius)
float area_normal_radius,
float start_strength)
{
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
ss->filter_cache = MEM_cnew<FilterCache>(__func__);
ss->filter_cache->start_filter_strength = start_strength;
ss->filter_cache->random_seed = rand();
if (undo_type == SCULPT_UNDO_COLOR) {
@ -338,6 +348,15 @@ static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type)
MESH_FILTER_SHARPEN);
}
static bool sculpt_mesh_filter_is_continuous(eSculptMeshFilterType type)
{
return (ELEM(type,
MESH_FILTER_SHARPEN,
MESH_FILTER_SMOOTH,
MESH_FILTER_RELAX,
MESH_FILTER_RELAX_FACE_SETS));
}
static void mesh_filter_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
@ -379,7 +398,8 @@ static void mesh_filter_task_cb(void *__restrict userdata,
continue;
}
if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) {
if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS) ||
ss->filter_cache->no_orig_co) {
copy_v3_v3(orig_co, vd.co);
}
else {
@ -681,34 +701,16 @@ static void mesh_filter_surface_smooth_displace_task_cb(void *__restrict userdat
BKE_pbvh_vertex_iter_end;
}
static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
float filter_strength = RNA_float_get(op->ptr, "strength");
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
SCULPT_filter_cache_free(ss);
SCULPT_undo_push_end(ob);
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
return OPERATOR_FINISHED;
}
if (event->type != MOUSEMOVE) {
return OPERATOR_RUNNING_MODAL;
}
const float len = event->prev_press_xy[0] - event->xy[0];
filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC;
SCULPT_vertex_random_access_ensure(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
@ -740,22 +742,136 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
}
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
}
static void sculpt_mesh_update_strength(wmOperator *op,
SculptSession *ss,
float2 prev_press_mouse,
float2 mouse)
{
const float len = prev_press_mouse[0] - mouse[0];
float filter_strength = ss->filter_cache->start_filter_strength * -len * 0.001f * UI_DPI_FAC;
RNA_float_set(op->ptr, "strength", filter_strength);
}
static void sculpt_mesh_filter_apply_with_history(bContext *C, wmOperator *op)
{
/* Event history is only stored for smooth and relax filters. */
if (!RNA_collection_length(op->ptr, "event_history")) {
sculpt_mesh_filter_apply(C, op);
return;
}
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
float2 start_mouse;
bool first = true;
float initial_strength = ss->filter_cache->start_filter_strength;
RNA_BEGIN (op->ptr, item, "event_history") {
float2 mouse;
RNA_float_get_array(&item, "mouse_event", mouse);
if (first) {
first = false;
start_mouse = mouse;
continue;
}
sculpt_mesh_update_strength(op, ss, start_mouse, mouse);
sculpt_mesh_filter_apply(C, op);
}
RNA_END;
RNA_float_set(op->ptr, "strength", initial_strength);
}
static void sculpt_mesh_filter_end(bContext *C, wmOperator * /*op*/)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
SCULPT_filter_cache_free(ss);
SCULPT_undo_push_end(ob);
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
}
static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
SculptSession *ss = ob->sculpt;
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
float initial_strength = ss->filter_cache->start_filter_strength;
sculpt_mesh_filter_end(C, op);
/* Don't update strength property if we're storing an event history. */
if (sculpt_mesh_filter_is_continuous(filter_type)) {
RNA_float_set(op->ptr, "strength", initial_strength);
}
return OPERATOR_FINISHED;
}
if (event->type != MOUSEMOVE) {
return OPERATOR_RUNNING_MODAL;
}
/* Note: some filter types are continuous, for these we store an
* event history in RNA for continuous.
* This way the user can tweak the last operator properties
* or repeat the op and get expected results. */
if (sculpt_mesh_filter_is_continuous(filter_type)) {
if (RNA_collection_length(op->ptr, "event_history") == 0) {
/* First entry is the start mouse position, event->prev_press_xy. */
PointerRNA startptr;
RNA_collection_add(op->ptr, "event_history", &startptr);
float2 mouse_start(float(event->prev_press_xy[0]), float(event->prev_press_xy[1]));
RNA_float_set_array(&startptr, "mouse_event", mouse_start);
}
PointerRNA itemptr;
RNA_collection_add(op->ptr, "event_history", &itemptr);
float2 mouse(float(event->xy[0]), float(event->xy[1]));
RNA_float_set_array(&itemptr, "mouse_event", mouse);
RNA_float_set(&itemptr, "pressure", WM_event_tablet_data(event, nullptr, nullptr));
}
float2 prev_mval(float(event->prev_press_xy[0]), float(event->prev_press_xy[1]));
float2 mval(float(event->xy[0]), float(event->xy[1]));
sculpt_mesh_update_strength(op, ss, prev_mval, mval);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
sculpt_mesh_filter_apply(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* Returns OPERATOR_PASS_THROUGH on success. */
static int sculpt_mesh_filter_start(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int mval[2];
RNA_int_get_array(op->ptr, "start_mouse", mval);
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
const bool use_automasking = SCULPT_is_automasking_enabled(sd, nullptr, nullptr);
const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking;
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false);
SculptSession *ss = ob->sculpt;
const eMeshFilterDeformAxis deform_axis = eMeshFilterDeformAxis(
RNA_enum_get(op->ptr, "deform_axis"));
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, nullptr);
const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking;
if (deform_axis == 0) {
/* All axis are disabled, so the filter is not going to produce any deformation. */
@ -768,21 +884,25 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
/* Update the active face set manually as the paint cursor is not enabled when using the Mesh
* Filter Tool. */
float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
float mval_fl[2] = {float(mval[0]), float(mval[1])};
SculptCursorGeometryInfo sgi;
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
}
SCULPT_vertex_random_access_ensure(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false);
if (needs_topology_info) {
SCULPT_boundary_info_ensure(ob);
}
SCULPT_undo_push_begin(ob, op);
SCULPT_filter_cache_init(
C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
SCULPT_filter_cache_init(C,
ob,
sd,
SCULPT_UNDO_COORDS,
mval,
RNA_float_get(op->ptr, "area_normal_radius"),
RNA_float_get(op->ptr, "strength"));
FilterCache *filter_cache = ss->filter_cache;
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
@ -826,12 +946,56 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
RNA_enum_get(op->ptr, "orientation"));
ss->filter_cache->orientation = orientation;
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
return OPERATOR_PASS_THROUGH;
}
static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
RNA_int_set_array(op->ptr, "start_mouse", event->mval);
int ret = sculpt_mesh_filter_start(C, op);
if (ret == OPERATOR_PASS_THROUGH) {
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
return ret;
}
static int sculpt_mesh_filter_exec(bContext *C, wmOperator *op)
{
int ret = sculpt_mesh_filter_start(C, op);
if (ret == OPERATOR_PASS_THROUGH) {
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
int iterations = RNA_int_get(op->ptr, "iteration_count");
bool has_history = RNA_collection_length(op->ptr, "event_history") > 0;
if (!has_history) {
ss->filter_cache->no_orig_co = true;
}
for (int i = 0; i < iterations; i++) {
sculpt_mesh_filter_apply_with_history(C, op);
ss->filter_cache->no_orig_co = true;
}
sculpt_mesh_filter_end(C, op);
return OPERATOR_FINISHED;
}
return ret;
}
void SCULPT_mesh_filter_properties(wmOperatorType *ot)
{
RNA_def_int_array(
ot->srna, "start_mouse", 2, nullptr, 0, 1 << 14, "Starting Mouse", "", 0, 1 << 14);
RNA_def_float(
ot->srna,
"area_normal_radius",
@ -844,6 +1008,31 @@ void SCULPT_mesh_filter_properties(wmOperatorType *ot)
1.0);
RNA_def_float(
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
RNA_def_int(ot->srna,
"iteration_count",
1,
1,
10000,
"Repeat",
"How many times to repeat the filter",
1,
100);
/* Smooth filter requires entire event history. */
PropertyRNA *prop = RNA_def_collection_runtime(
ot->srna, "event_history", &RNA_OperatorStrokeElement, "", "");
RNA_def_property_flag(prop, PropertyFlag(int(PROP_HIDDEN) | int(PROP_SKIP_SAVE)));
}
static void sculpt_mesh_ui_exec(bContext * /*C*/, wmOperator *op)
{
uiLayout *layout = op->layout;
uiItemR(layout, op->ptr, "strength", 0, nullptr, ICON_NONE);
uiItemR(layout, op->ptr, "iteration_count", 0, nullptr, ICON_NONE);
uiItemR(layout, op->ptr, "orientation", 0, nullptr, ICON_NONE);
layout = uiLayoutRow(layout, true);
uiItemR(layout, op->ptr, "deform_axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
void SCULPT_OT_mesh_filter(wmOperatorType *ot)
@ -857,6 +1046,8 @@ void SCULPT_OT_mesh_filter(wmOperatorType *ot)
ot->invoke = sculpt_mesh_filter_invoke;
ot->modal = sculpt_mesh_filter_modal;
ot->poll = SCULPT_mode_poll;
ot->exec = sculpt_mesh_filter_exec;
ot->ui = sculpt_mesh_ui_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

View File

@ -494,6 +494,8 @@ struct FilterCache {
float (*pre_smoothed_color)[4];
ViewContext vc;
float start_filter_strength;
bool no_orig_co;
};
/**
@ -1427,7 +1429,8 @@ void SCULPT_filter_cache_init(bContext *C,
Sculpt *sd,
int undo_type,
const int mval[2],
float area_normal_radius);
float area_normal_radius,
float start_strength);
void SCULPT_filter_cache_free(SculptSession *ss);
void SCULPT_mesh_filter_properties(wmOperatorType *ot);

View File

@ -229,6 +229,8 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
SCULPT_topology_islands_invalidate(ss);
/* Redraw. */
SCULPT_pbvh_clear(ob);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);

View File

@ -58,7 +58,7 @@ void ED_sculpt_init_transform(bContext *C, Object *ob, const int mval[2], const
SCULPT_vertex_random_access_ensure(ss);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0, 1.0f);
if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) {
ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL;

View File

@ -590,7 +590,7 @@ void ED_animedit_unlink_action(
if (nlt->strips.first == NULL) {
BLI_assert(nstrip == NULL);
BKE_nlatrack_free(&adt->nla_tracks, nlt, true);
BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true);
}
}
}

View File

@ -128,16 +128,55 @@ static void draw_tile_background(const rcti *draw_rect, int colorid, int shade)
UI_draw_roundbox_aa(&draw_rect_fl, true, 5.0f, color);
}
static void file_draw_icon(const SpaceFile *sfile,
uiBlock *block,
const FileDirEntry *file,
const char *path,
const rcti *tile_draw_rect,
int icon,
int width,
int height,
bool drag,
bool dimmed)
static void file_but_enable_drag(uiBut *but,
const SpaceFile *sfile,
const FileDirEntry *file,
const char *path,
ImBuf *preview_image,
int icon,
float scale)
{
ID *id;
if ((id = filelist_file_get_id(file))) {
UI_but_drag_set_id(but, id);
if (preview_image) {
UI_but_drag_attach_image(but, preview_image, scale);
}
}
else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS &&
(file->typeflag & FILE_TYPE_ASSET) != 0) {
char blend_path[FILE_MAX_LIBEXTRA];
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
const int import_method = ED_fileselect_asset_import_method_get(sfile, file);
BLI_assert(import_method > -1);
UI_but_drag_set_asset(but,
&(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
import_method,
icon,
preview_image,
scale);
}
}
else if (preview_image) {
UI_but_drag_set_image(but, BLI_strdup(path), icon, preview_image, scale, true);
}
else {
/* path is no more static, cannot give it directly to but... */
UI_but_drag_set_path(but, BLI_strdup(path), true);
}
}
static uiBut *file_add_icon_but(const SpaceFile *sfile,
uiBlock *block,
const char *path,
const rcti *tile_draw_rect,
int icon,
int width,
int height,
bool dimmed)
{
uiBut *but;
@ -151,42 +190,7 @@ static void file_draw_icon(const SpaceFile *sfile,
block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, a1, a2, NULL);
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN);
if (drag) {
/* TODO: duplicated from file_draw_preview(). */
ID *id;
if ((id = filelist_file_get_id(file))) {
UI_but_drag_set_id(but, id);
ImBuf *preview_image = filelist_file_getimage(file);
if (preview_image) {
UI_but_drag_attach_image(but, preview_image, UI_DPI_FAC);
}
}
else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS &&
(file->typeflag & FILE_TYPE_ASSET) != 0) {
ImBuf *preview_image = filelist_file_getimage(file);
char blend_path[FILE_MAX_LIBEXTRA];
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
BLI_assert(asset_params != NULL);
const int import_method = ED_fileselect_asset_import_method_get(sfile, file);
BLI_assert(import_method > -1);
UI_but_drag_set_asset(but,
&(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
import_method,
icon,
preview_image,
UI_DPI_FAC);
}
}
else {
/* path is no more static, cannot give it directly to but... */
UI_but_drag_set_path(but, BLI_strdup(path), true);
}
}
return but;
}
static void file_draw_string(int sx,
@ -297,21 +301,50 @@ void file_calc_previews(const bContext *C, ARegion *region)
UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height);
}
static void file_draw_preview(const SpaceFile *sfile,
uiBlock *block,
const FileDirEntry *file,
const char *path,
static void file_add_preview_drag_but(const SpaceFile *sfile,
uiBlock *block,
FileLayout *layout,
const FileDirEntry *file,
const char *path,
const rcti *tile_draw_rect,
ImBuf *preview_image,
const int icon,
const float scale)
{
/* Invisible button for dragging. */
rcti drag_rect = *tile_draw_rect;
/* A bit smaller than the full tile, to increase the gap between items that users can drag from
* for box select. */
BLI_rcti_pad(&drag_rect, -layout->tile_border_x, -layout->tile_border_y);
uiBut *but = uiDefBut(block,
UI_BTYPE_LABEL,
0,
"",
drag_rect.xmin,
drag_rect.ymin,
BLI_rcti_size_x(&drag_rect),
BLI_rcti_size_y(&drag_rect),
NULL,
0.0,
0.0,
0,
0,
NULL);
file_but_enable_drag(but, sfile, file, path, preview_image, icon, scale);
}
static void file_draw_preview(const FileDirEntry *file,
const rcti *tile_draw_rect,
const float icon_aspect,
ImBuf *imb,
const int icon,
FileLayout *layout,
const bool is_icon,
const bool drag,
const bool dimmed,
const bool is_link)
const bool is_link,
float *r_scale)
{
uiBut *but;
float fx, fy;
float dx, dy;
int xco, yco;
@ -528,62 +561,11 @@ static void file_draw_preview(const SpaceFile *sfile,
immUnbindProgram();
}
/* Invisible button for dragging. */
rcti drag_rect = *tile_draw_rect;
/* A bit smaller than the full tile, to increase the gap between items that users can drag from
* for box select. */
BLI_rcti_pad(&drag_rect, -layout->tile_border_x, -layout->tile_border_y);
but = uiDefBut(block,
UI_BTYPE_LABEL,
0,
"",
drag_rect.xmin,
drag_rect.ymin,
BLI_rcti_size_x(&drag_rect),
BLI_rcti_size_y(&drag_rect),
NULL,
0.0,
0.0,
0,
0,
NULL);
/* Drag-region. */
if (drag) {
ID *id;
if ((id = filelist_file_get_id(file))) {
UI_but_drag_set_id(but, id);
UI_but_drag_attach_image(but, imb, scale);
}
/* path is no more static, cannot give it directly to but... */
else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS &&
(file->typeflag & FILE_TYPE_ASSET) != 0) {
char blend_path[FILE_MAX_LIBEXTRA];
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
BLI_assert(asset_params != NULL);
const int import_method = ED_fileselect_asset_import_method_get(sfile, file);
BLI_assert(import_method > -1);
UI_but_drag_set_asset(but,
&(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
import_method,
icon,
imb,
scale);
}
}
else {
UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
}
}
GPU_blend(GPU_BLEND_NONE);
if (r_scale) {
*r_scale = scale;
}
}
static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
@ -1036,31 +1018,36 @@ void file_draw_list(const bContext *C, ARegion *region)
is_icon = 1;
}
file_draw_preview(sfile,
block,
file,
path,
float scale = 0;
file_draw_preview(file,
&tile_draw_rect,
thumb_icon_aspect,
imb,
icon,
layout,
is_icon,
do_drag,
is_hidden,
is_link);
is_link,
/* Returns the scale which is needed below. */
&scale);
if (do_drag) {
file_add_preview_drag_but(
sfile, block, layout, file, path, &tile_draw_rect, imb, icon, scale);
}
}
else {
file_draw_icon(sfile,
block,
file,
path,
&tile_draw_rect,
filelist_geticon(files, i, true),
ICON_DEFAULT_WIDTH_SCALE,
ICON_DEFAULT_HEIGHT_SCALE,
do_drag,
is_hidden);
const int icon = filelist_geticon(files, i, true);
uiBut *icon_but = file_add_icon_but(sfile,
block,
path,
&tile_draw_rect,
icon,
ICON_DEFAULT_WIDTH_SCALE,
ICON_DEFAULT_HEIGHT_SCALE,
is_hidden);
if (do_drag) {
file_but_enable_drag(icon_but, sfile, file, path, NULL, icon, UI_DPI_FAC);
}
icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
}

View File

@ -736,7 +736,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op))
}
/* call delete on this track - deletes all strips too */
BKE_nlatrack_free(&adt->nla_tracks, nlt, true);
BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true);
ale->update = ANIM_UPDATE_DEPS;
}
}

View File

@ -60,7 +60,7 @@ bool space_node_view_flag(
int tot = 0;
bool has_frame = false;
if (snode.edittree) {
LISTBASE_FOREACH (const bNode *, node, &snode.edittree->nodes) {
for (const bNode *node : snode.edittree->all_nodes()) {
if ((node->flag & node_flag) == node_flag) {
BLI_rctf_union(&cur_new, &node->runtime->totr);
tot++;

View File

@ -839,7 +839,6 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
options.propagation_info,
attributes_to_propagate);
attributes_to_propagate.remove("position");
attributes_to_propagate.remove("normal");
attributes_to_propagate.remove("shade_smooth");
r_create_id = attributes_to_propagate.pop_try("id").has_value();
r_create_material_index = attributes_to_propagate.pop_try("material_index").has_value();

View File

@ -640,6 +640,10 @@ void VKShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
build_shader_module(sources, shaderc_compute_shader, &compute_module_);
}
void VKShader::warm_cache(int /*limit*/)
{
}
bool VKShader::finalize(const shader::ShaderCreateInfo *info)
{
if (compilation_failed_) {

View File

@ -35,7 +35,7 @@ class VKShader : public Shader {
void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
void compute_shader_from_glsl(MutableSpan<const char *> sources) override;
bool finalize(const shader::ShaderCreateInfo *info = nullptr) override;
void warm_cache(int limit) override{};
void warm_cache(int limit) override;
void transform_feedback_names_set(Span<const char *> name_list,
eGPUShaderTFBType geom_type) override;

View File

@ -593,7 +593,7 @@ static void rna_NlaTrack_remove(
return;
}
BKE_nlatrack_free(&adt->nla_tracks, track, true);
BKE_nlatrack_remove_and_free(&adt->nla_tracks, track, true);
RNA_POINTER_INVALIDATE(track_ptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL);

View File

@ -8,13 +8,13 @@
/* Macros to help reduce code clutter in rna_mesh.c */
/* Define the accessors for a basic CustomDataLayer collection */
/* Define the accessors for a basic CustomDataLayer collection, skipping anonymous layers */
#define DEFINE_CUSTOMDATA_LAYER_COLLECTION(collection_name, customdata_type, layer_type) \
/* check */ \
static int rna_##collection_name##_check(CollectionPropertyIterator *UNUSED(iter), void *data) \
{ \
CustomDataLayer *layer = (CustomDataLayer *)data; \
return (layer->type != layer_type); \
return (layer->anonymous_id != NULL || layer->type != layer_type); \
} \
/* begin */ \
static void rna_Mesh_##collection_name##s_begin(CollectionPropertyIterator *iter, \
@ -37,7 +37,9 @@
static int rna_Mesh_##collection_name##s_length(PointerRNA *ptr) \
{ \
CustomData *data = rna_mesh_##customdata_type(ptr); \
return data ? CustomData_number_of_layers(data, layer_type) : 0; \
return data ? CustomData_number_of_layers(data, layer_type) - \
CustomData_number_of_anonymous_layers(data, layer_type) : \
0; \
} \
/* index range */ \
static void rna_Mesh_##collection_name##_index_range( \
@ -45,7 +47,9 @@
{ \
CustomData *data = rna_mesh_##customdata_type(ptr); \
*min = 0; \
*max = data ? CustomData_number_of_layers(data, layer_type) - 1 : 0; \
*max = data ? CustomData_number_of_layers(data, layer_type) - \
CustomData_number_of_anonymous_layers(data, layer_type) - 1 : \
0; \
*max = MAX2(0, *max); \
}

View File

@ -64,18 +64,6 @@ struct IndexAttributes {
/** \name Utility Functions
* \{ */
static Map<AttributeIDRef, AttributeKind> gather_attributes_without_id(
const GeometrySet &geometry_set,
const GeometryComponentType component_type,
const AnonymousAttributePropagationInfo &propagation_info)
{
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{component_type}, component_type, false, propagation_info, attributes);
attributes.remove("id");
return attributes;
};
static OffsetIndices<int> accumulate_counts_to_offsets(const IndexMask selection,
const VArray<int> &counts,
Array<int> &r_offset_data)
@ -183,39 +171,25 @@ static void copy_stable_id_point(const OffsetIndices<int> offsets,
dst_attribute.finish();
}
static void copy_attributes_without_id(GeometrySet &geometry_set,
const GeometryComponentType component_type,
const eAttrDomain domain,
const OffsetIndices<int> offsets,
static void copy_attributes_without_id(const OffsetIndices<int> offsets,
const IndexMask selection,
const AnonymousAttributePropagationInfo &propagation_info,
const eAttrDomain domain,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
{
const Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, component_type, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute || src_attribute.domain != domain) {
continue;
}
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
dst_attributes,
ATTR_DOMAIN_AS_MASK(domain),
propagation_info,
{"id"})) {
attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> src = src_attribute.varray.typed<T>();
MutableSpan<T> dst = dst_attribute.span.typed<T>();
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
threaded_slice_fill<T>(offsets, selection, src, dst);
});
dst_attribute.finish();
attribute.dst.finish();
}
}
@ -230,42 +204,26 @@ static void copy_attributes_without_id(GeometrySet &geometry_set,
* copied with an offset fill, otherwise a mapping is used.
*/
static void copy_curve_attributes_without_id(
const GeometrySet &geometry_set,
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const OffsetIndices<int> curve_offsets,
const AnonymousAttributePropagationInfo &propagation_info,
bke::CurvesGeometry &dst_curves)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE, propagation_info);
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute =
dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_curves.attributes(),
dst_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_ALL,
propagation_info,
{"id"})) {
attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> src{src_attribute.varray.typed<T>()};
MutableSpan<T> dst = dst_attribute.span.typed<T>();
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (out_domain) {
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_CURVE:
threaded_slice_fill<T>(curve_offsets, selection, src, dst);
break;
@ -281,10 +239,11 @@ static void copy_curve_attributes_without_id(
});
break;
default:
BLI_assert_unreachable();
break;
}
});
dst_attribute.finish();
attribute.dst.finish();
}
}
@ -394,8 +353,7 @@ static void duplicate_curves(GeometrySet &geometry_set,
});
all_dst_offsets.last() = dst_points_num;
copy_curve_attributes_without_id(
geometry_set, curves, selection, curve_offsets, propagation_info, new_curves);
copy_curve_attributes_without_id(curves, selection, curve_offsets, propagation_info, new_curves);
copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
@ -422,7 +380,6 @@ static void duplicate_curves(GeometrySet &geometry_set,
* copied with an offset fill, otherwise a mapping is used.
*/
static void copy_face_attributes_without_id(
GeometrySet &geometry_set,
const Span<int> edge_mapping,
const Span<int> vert_mapping,
const Span<int> loop_mapping,
@ -432,31 +389,14 @@ static void copy_face_attributes_without_id(
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_MESH, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_ALL, propagation_info, {"id"})) {
attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> src{src_attribute.varray.typed<T>()};
MutableSpan<T> dst = dst_attribute.span.typed<T>();
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (out_domain) {
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_POINT:
array_utils::gather(src, vert_mapping, dst);
break;
@ -470,10 +410,11 @@ static void copy_face_attributes_without_id(
array_utils::gather(src, loop_mapping, dst);
break;
default:
BLI_assert_unreachable();
break;
}
});
dst_attribute.finish();
attribute.dst.finish();
}
}
@ -607,8 +548,7 @@ static void duplicate_faces(GeometrySet &geometry_set,
new_mesh->loose_edges_tag_none();
copy_face_attributes_without_id(geometry_set,
edge_mapping,
copy_face_attributes_without_id(edge_mapping,
vert_mapping,
loop_mapping,
duplicates,
@ -646,7 +586,6 @@ static void duplicate_faces(GeometrySet &geometry_set,
* copied with an offset fill, for point domain a mapping is used.
*/
static void copy_edge_attributes_without_id(
GeometrySet &geometry_set,
const Span<int> point_mapping,
const OffsetIndices<int> offsets,
const IndexMask selection,
@ -654,30 +593,18 @@ static void copy_edge_attributes_without_id(
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_MESH, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
const eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
for (auto &attribute :
bke::retrieve_attributes_for_transfer(src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_EDGE,
propagation_info,
{"id"})) {
attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> src{src_attribute.varray.typed<T>()};
MutableSpan<T> dst = dst_attribute.span.typed<T>();
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (out_domain) {
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_EDGE:
threaded_slice_fill<T>(offsets, selection, src, dst);
break;
@ -685,10 +612,11 @@ static void copy_edge_attributes_without_id(
array_utils::gather(src, point_mapping, dst);
break;
default:
BLI_assert_unreachable();
break;
}
});
dst_attribute.finish();
attribute.dst.finish();
}
}
@ -791,8 +719,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
}
});
copy_edge_attributes_without_id(geometry_set,
vert_orig_indices,
copy_edge_attributes_without_id(vert_orig_indices,
duplicates,
selection,
propagation_info,
@ -855,32 +782,16 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
}
new_curve_offsets.last() = dst_num;
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
eAttrDomain domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute =
new_curves.attributes_for_write().lookup_or_add_for_write_only_span(
attribute_id, domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_ALL,
propagation_info,
{"id"})) {
attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> src{src_attribute.varray.typed<T>()};
MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (domain) {
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_CURVE:
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
@ -893,10 +804,11 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
threaded_slice_fill(duplicates, selection, src, dst);
break;
default:
BLI_assert_unreachable();
break;
}
});
dst_attribute.finish();
attribute.dst.finish();
}
copy_stable_id_point(duplicates, src_curves.attributes(), new_curves.attributes_for_write());
@ -940,12 +852,10 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
Mesh *new_mesh = BKE_mesh_new_nomain(duplicates.total_size(), 0, 0, 0, 0);
copy_attributes_without_id(geometry_set,
GEO_COMPONENT_TYPE_MESH,
ATTR_DOMAIN_POINT,
duplicates,
copy_attributes_without_id(duplicates,
selection,
propagation_info,
ATTR_DOMAIN_POINT,
mesh.attributes(),
new_mesh->attributes_for_write());
@ -990,12 +900,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(duplicates.total_size());
copy_attributes_without_id(geometry_set,
GEO_COMPONENT_TYPE_POINT_CLOUD,
ATTR_DOMAIN_POINT,
duplicates,
copy_attributes_without_id(duplicates,
selection,
propagation_info,
ATTR_DOMAIN_POINT,
src_points.attributes(),
pointcloud->attributes_for_write());
@ -1103,12 +1011,10 @@ static void duplicate_instances(GeometrySet &geometry_set,
dst_instances->reference_handles().slice(range).fill(new_handle);
}
copy_attributes_without_id(geometry_set,
GEO_COMPONENT_TYPE_INSTANCES,
ATTR_DOMAIN_INSTANCE,
duplicates,
copy_attributes_without_id(duplicates,
selection,
propagation_info,
ATTR_DOMAIN_INSTANCE,
src_instances.attributes(),
dst_instances->attributes_for_write());

View File

@ -471,9 +471,6 @@ static void extrude_mesh_edges(Mesh &mesh,
}
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
}
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
@ -867,9 +864,6 @@ static void extrude_mesh_face_regions(Mesh &mesh,
}
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
}
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
@ -1158,9 +1152,6 @@ static void extrude_individual_mesh_faces(Mesh &mesh,
}
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
}
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);

View File

@ -78,16 +78,14 @@ bool node_group_poll_instance(const bNode *node,
const bNodeTree *nodetree,
const char **disabled_hint)
{
if (node->typeinfo->poll(node->typeinfo, nodetree, disabled_hint)) {
const bNodeTree *grouptree = (const bNodeTree *)node->id;
if (grouptree) {
return nodeGroupPoll(nodetree, grouptree, disabled_hint);
}
return true; /* without a linked node tree, group node is always ok */
if (!node->typeinfo->poll(node->typeinfo, nodetree, disabled_hint)) {
return false;
}
return false;
const bNodeTree *grouptree = reinterpret_cast<const bNodeTree *>(node->id);
if (!grouptree) {
return true;
}
return nodeGroupPoll(nodetree, grouptree, disabled_hint);
}
bool nodeGroupPoll(const bNodeTree *nodetree,
@ -114,10 +112,9 @@ bool nodeGroupPoll(const bNodeTree *nodetree,
return false;
}
LISTBASE_FOREACH (const bNode *, node, &grouptree->nodes) {
for (const bNode *node : grouptree->all_nodes()) {
if (node->typeinfo->poll_instance &&
!node->typeinfo->poll_instance(
const_cast<bNode *>(node), const_cast<bNodeTree *>(nodetree), r_disabled_hint)) {
!node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) {
return false;
}
}