Merge branch 'refactor-mesh-position-generic' into refactor-mesh-corners-generic
This commit is contained in:
commit
74f0b3b909
|
@ -1670,7 +1670,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
|||
col.label(text="and Windows driver version 101.3430 or newer", icon='BLANK1')
|
||||
elif sys.platform.startswith("linux"):
|
||||
col.label(text="Requires Intel GPU with Xe-HPG architecture and", icon='BLANK1')
|
||||
col.label(text=" - Linux driver version xx.xx.23904 or newer", icon='BLANK1')
|
||||
col.label(text=" - intel-level-zero-gpu version 1.3.23904 or newer", icon='BLANK1')
|
||||
col.label(text=" - oneAPI Level-Zero Loader", icon='BLANK1')
|
||||
elif device_type == 'METAL':
|
||||
col.label(text="Requires Apple Silicon with macOS 12.2 or newer", icon='BLANK1')
|
||||
|
|
|
@ -796,11 +796,11 @@ ccl_device float bits_to_01(uint bits)
|
|||
ccl_device_inline uint popcount(uint x)
|
||||
{
|
||||
/* TODO(Stefan): pop-count intrinsic for Windows with fallback for older CPUs. */
|
||||
uint i = x & 0xaaaaaaaa;
|
||||
uint i = x;
|
||||
i = i - ((i >> 1) & 0x55555555);
|
||||
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
|
||||
i = (((i + (i >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
|
||||
return i & 1;
|
||||
return i;
|
||||
}
|
||||
# endif
|
||||
#elif defined(__KERNEL_ONEAPI__)
|
||||
|
|
|
@ -101,7 +101,7 @@ bool win32_chk(bool result, const char *file, int line, const char *text)
|
|||
|
||||
# ifndef NDEBUG
|
||||
_ftprintf(
|
||||
stderr, "%s(%d):[%s] -> Win32 Error# (%lu): %s", file, line, text, ulong(error), msg);
|
||||
stderr, "%s:%d: [%s] -> Win32 Error# (%lu): %s", file, line, text, ulong(error), msg);
|
||||
# else
|
||||
_ftprintf(stderr, "Win32 Error# (%lu): %s", ulong(error), msg);
|
||||
# endif
|
||||
|
|
|
@ -123,7 +123,7 @@ static bool egl_chk(bool result,
|
|||
|
||||
#ifndef NDEBUG
|
||||
fprintf(stderr,
|
||||
"%s(%d):[%s] -> EGL Error (0x%04X): %s: %s\n",
|
||||
"%s:%d: [%s] -> EGL Error (0x%04X): %s: %s\n",
|
||||
file,
|
||||
line,
|
||||
text,
|
||||
|
|
|
@ -1049,9 +1049,9 @@ void GHOST_SystemWin32::processPointerEvent(
|
|||
}
|
||||
}
|
||||
|
||||
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
|
||||
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window,
|
||||
const int32_t screen_co[2])
|
||||
{
|
||||
int32_t x_screen, y_screen;
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
if (window->getTabletData().Active != GHOST_kTabletModeNone) {
|
||||
|
@ -1059,8 +1059,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
|
|||
return NULL;
|
||||
}
|
||||
|
||||
system->getCursorPosition(x_screen, y_screen);
|
||||
|
||||
int32_t x_screen = screen_co[0], y_screen = screen_co[1];
|
||||
if (window->getCursorGrabModeIsWarp()) {
|
||||
/* WORKAROUND:
|
||||
* Sometimes Windows ignores `SetCursorPos()` or `SendInput()` calls or the mouse event is
|
||||
|
@ -1834,7 +1833,10 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
|
|||
}
|
||||
}
|
||||
|
||||
event = processCursorEvent(window);
|
||||
const int32_t window_co[2] = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
|
||||
int32_t screen_co[2];
|
||||
window->clientToScreen(UNPACK2(window_co), UNPACK2(screen_co));
|
||||
event = processCursorEvent(window, screen_co);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -1876,7 +1878,10 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
|
|||
WINTAB_PRINTF("HWND %p mouse leave\n", window->getHWND());
|
||||
window->m_mousePresent = false;
|
||||
if (window->getTabletData().Active == GHOST_kTabletModeNone) {
|
||||
event = processCursorEvent(window);
|
||||
/* FIXME: document why the cursor motion event on mouse leave is needed. */
|
||||
int32_t screen_co[2] = {0, 0};
|
||||
system->getCursorPosition(screen_co[0], screen_co[1]);
|
||||
event = processCursorEvent(window, screen_co);
|
||||
}
|
||||
GHOST_Wintab *wt = window->getWintab();
|
||||
if (wt) {
|
||||
|
|
|
@ -331,7 +331,8 @@ class GHOST_SystemWin32 : public GHOST_System {
|
|||
* \param window: The window receiving the event (the active window).
|
||||
* \return The event created.
|
||||
*/
|
||||
static GHOST_EventCursor *processCursorEvent(GHOST_WindowWin32 *window);
|
||||
static GHOST_EventCursor *processCursorEvent(GHOST_WindowWin32 *window,
|
||||
const int32_t screen_co[2]);
|
||||
|
||||
/**
|
||||
* Handles a mouse wheel event.
|
||||
|
|
|
@ -40,6 +40,25 @@
|
|||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="3.4" date="2022-12-07">
|
||||
<description>
|
||||
<p>New features:</p>
|
||||
<ul>
|
||||
<li>Cycles Path Guiding</li>
|
||||
<li>Sculpt geometry-based relax brush</li>
|
||||
<li>Viewport overlay for Geometry Nodes Viewer Node</li>
|
||||
<li>EEVEE headless rendering support on Linux</li>
|
||||
</ul>
|
||||
<p>Enhancements:</p>
|
||||
<ul>
|
||||
<li>Many new Geometry Nodes for meshes, curves and more</li>
|
||||
<li>NLA editor usability improvements</li>
|
||||
<li>FFmpeg AV1 codec encoding</li>
|
||||
<li>Improved font thumbnails</li>
|
||||
<li>Blender as a Python Module</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="3.3" date="2022-09-07">
|
||||
<description>
|
||||
<p>New features:</p>
|
||||
|
|
|
@ -2137,9 +2137,6 @@ def km_node_editor(params):
|
|||
)),
|
||||
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
|
||||
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
|
||||
("node.move_detach_links",
|
||||
{"type": 'D', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
|
||||
("node.move_detach_links_release",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "alt": True},
|
||||
{"properties": [("NODE_OT_translate_attach", [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])])]}),
|
||||
|
|
|
@ -88,8 +88,16 @@ class CURVES_UL_attributes(UIList):
|
|||
flags = []
|
||||
indices = [i for i in range(len(attributes))]
|
||||
|
||||
for item in attributes:
|
||||
flags.append(0 if item.is_internal else self.bitflag_filter_item)
|
||||
# Filtering by name
|
||||
if self.filter_name:
|
||||
flags = bpy.types.UI_UL_list.filter_items_by_name(
|
||||
self.filter_name, self.bitflag_filter_item, attributes, "name", reverse=self.use_filter_invert)
|
||||
if not flags:
|
||||
flags = [self.bitflag_filter_item] * len(attributes)
|
||||
|
||||
# Filtering internal attributes
|
||||
for idx, item in enumerate(attributes):
|
||||
flags[idx] = 0 if item.is_internal else flags[idx]
|
||||
|
||||
return flags, indices
|
||||
|
||||
|
|
|
@ -537,8 +537,16 @@ class MESH_UL_attributes(UIList):
|
|||
flags = []
|
||||
indices = [i for i in range(len(attributes))]
|
||||
|
||||
for item in attributes:
|
||||
flags.append(0 if item.is_internal else self.bitflag_filter_item)
|
||||
# Filtering by name
|
||||
if self.filter_name:
|
||||
flags = bpy.types.UI_UL_list.filter_items_by_name(
|
||||
self.filter_name, self.bitflag_filter_item, attributes, "name", reverse=self.use_filter_invert)
|
||||
if not flags:
|
||||
flags = [self.bitflag_filter_item] * len(attributes)
|
||||
|
||||
# Filtering internal attributes
|
||||
for idx, item in enumerate(attributes):
|
||||
flags[idx] = 0 if item.is_internal else flags[idx]
|
||||
|
||||
return flags, indices
|
||||
|
||||
|
@ -626,20 +634,26 @@ class ColorAttributesListBase():
|
|||
}
|
||||
|
||||
def filter_items(self, _context, data, property):
|
||||
attrs = getattr(data, property)
|
||||
ret = []
|
||||
idxs = []
|
||||
attributes = getattr(data, property)
|
||||
flags = []
|
||||
indices = [i for i in range(len(attributes))]
|
||||
|
||||
for idx, item in enumerate(attrs):
|
||||
# Filtering by name
|
||||
if self.filter_name:
|
||||
flags = bpy.types.UI_UL_list.filter_items_by_name(
|
||||
self.filter_name, self.bitflag_filter_item, attributes, "name", reverse=self.use_filter_invert)
|
||||
if not flags:
|
||||
flags = [self.bitflag_filter_item] * len(attributes)
|
||||
|
||||
for idx, item in enumerate(attributes):
|
||||
skip = (
|
||||
(item.domain not in {"POINT", "CORNER"}) or
|
||||
(item.data_type not in {"FLOAT_COLOR", "BYTE_COLOR"}) or
|
||||
item.is_internal
|
||||
)
|
||||
ret.append(0 if skip else self.bitflag_filter_item)
|
||||
idxs.append(idx)
|
||||
flags[idx] = 0 if skip else flags[idx]
|
||||
|
||||
return ret, idxs
|
||||
return flags, indices
|
||||
|
||||
|
||||
class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
|
||||
|
|
|
@ -70,8 +70,16 @@ class POINTCLOUD_UL_attributes(UIList):
|
|||
flags = []
|
||||
indices = [i for i in range(len(attributes))]
|
||||
|
||||
for item in attributes:
|
||||
flags.append(0 if item.is_internal else self.bitflag_filter_item)
|
||||
# Filtering by name
|
||||
if self.filter_name:
|
||||
flags = bpy.types.UI_UL_list.filter_items_by_name(
|
||||
self.filter_name, self.bitflag_filter_item, attributes, "name", reverse=self.use_filter_invert)
|
||||
if not flags:
|
||||
flags = [self.bitflag_filter_item] * len(attributes)
|
||||
|
||||
# Filtering internal attributes
|
||||
for idx, item in enumerate(attributes):
|
||||
flags[idx] = 0 if item.is_internal else flags[idx]
|
||||
|
||||
return flags, indices
|
||||
|
||||
|
|
|
@ -51,6 +51,23 @@ static bool BLI_path_is_abs(const char *name);
|
|||
|
||||
// #define DEBUG_STRSIZE
|
||||
|
||||
/**
|
||||
* On UNIX it only makes sense to treat `/` as a path separator.
|
||||
* On WIN32 either may be used.
|
||||
*/
|
||||
static bool is_sep_native_compat(const char ch)
|
||||
{
|
||||
if (ch == SEP) {
|
||||
return true;
|
||||
}
|
||||
#ifdef WIN32
|
||||
if (ch == ALTSEP) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* implementation */
|
||||
|
||||
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_digits_len)
|
||||
|
@ -1450,7 +1467,7 @@ size_t BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__
|
|||
size_t dirlen = BLI_strnlen(dst, maxlen);
|
||||
|
||||
/* Inline #BLI_path_slash_ensure. */
|
||||
if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
|
||||
if ((dirlen > 0) && !is_sep_native_compat(dst[dirlen - 1])) {
|
||||
dst[dirlen++] = SEP;
|
||||
dst[dirlen] = '\0';
|
||||
}
|
||||
|
@ -1467,7 +1484,7 @@ size_t BLI_path_append_dir(char *__restrict dst, const size_t maxlen, const char
|
|||
size_t dirlen = BLI_path_append(dst, maxlen, dir);
|
||||
if (dirlen + 1 < maxlen) {
|
||||
/* Inline #BLI_path_slash_ensure. */
|
||||
if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
|
||||
if ((dirlen > 0) && !is_sep_native_compat(dst[dirlen - 1])) {
|
||||
dst[dirlen++] = SEP;
|
||||
dst[dirlen] = '\0';
|
||||
}
|
||||
|
@ -1522,7 +1539,7 @@ size_t BLI_path_join_array(char *__restrict dst,
|
|||
bool has_trailing_slash = false;
|
||||
if (ofs != 0) {
|
||||
size_t len = ofs;
|
||||
while ((len != 0) && (path[len - 1] == SEP)) {
|
||||
while ((len != 0) && is_sep_native_compat(path[len - 1])) {
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
|
@ -1536,18 +1553,18 @@ size_t BLI_path_join_array(char *__restrict dst,
|
|||
path = path_array[path_index];
|
||||
has_trailing_slash = false;
|
||||
const char *path_init = path;
|
||||
while (path[0] == SEP) {
|
||||
while (is_sep_native_compat(path[0])) {
|
||||
path++;
|
||||
}
|
||||
size_t len = strlen(path);
|
||||
if (len != 0) {
|
||||
while ((len != 0) && (path[len - 1] == SEP)) {
|
||||
while ((len != 0) && is_sep_native_compat(path[len - 1])) {
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
if (len != 0) {
|
||||
/* the very first path may have a slash at the end */
|
||||
if (ofs && (dst[ofs - 1] != SEP)) {
|
||||
if (ofs && !is_sep_native_compat(dst[ofs - 1])) {
|
||||
dst[ofs++] = SEP;
|
||||
if (ofs == dst_last) {
|
||||
break;
|
||||
|
@ -1570,7 +1587,7 @@ size_t BLI_path_join_array(char *__restrict dst,
|
|||
}
|
||||
|
||||
if (has_trailing_slash) {
|
||||
if ((ofs != dst_last) && (ofs != 0) && (dst[ofs - 1] != SEP)) {
|
||||
if ((ofs != dst_last) && (ofs != 0) && !is_sep_native_compat(dst[ofs - 1])) {
|
||||
dst[ofs++] = SEP;
|
||||
}
|
||||
}
|
||||
|
@ -1598,7 +1615,7 @@ static bool path_name_at_index_forward(const char *__restrict path,
|
|||
int i = 0;
|
||||
while (true) {
|
||||
const char c = path[i];
|
||||
if (ELEM(c, SEP, '\0')) {
|
||||
if ((c == '\0') || is_sep_native_compat(c)) {
|
||||
if (prev + 1 != i) {
|
||||
prev += 1;
|
||||
/* Skip '/./' (behave as if they don't exist). */
|
||||
|
@ -1633,7 +1650,7 @@ static bool path_name_at_index_backward(const char *__restrict path,
|
|||
int i = prev - 1;
|
||||
while (true) {
|
||||
const char c = i >= 0 ? path[i] : '\0';
|
||||
if (ELEM(c, SEP, '\0')) {
|
||||
if ((c == '\0') || is_sep_native_compat(c)) {
|
||||
if (prev - 1 != i) {
|
||||
i += 1;
|
||||
/* Skip '/./' (behave as if they don't exist). */
|
||||
|
@ -1732,7 +1749,7 @@ int BLI_path_slash_ensure(char *string, size_t string_maxlen)
|
|||
{
|
||||
int len = strlen(string);
|
||||
BLI_assert(len < string_maxlen);
|
||||
if (len == 0 || string[len - 1] != SEP) {
|
||||
if (len == 0 || !is_sep_native_compat(string[len - 1])) {
|
||||
/* Avoid unlikely buffer overflow. */
|
||||
if (len + 1 < string_maxlen) {
|
||||
string[len] = SEP;
|
||||
|
@ -1747,7 +1764,7 @@ void BLI_path_slash_rstrip(char *string)
|
|||
{
|
||||
int len = strlen(string);
|
||||
while (len) {
|
||||
if (string[len - 1] == SEP) {
|
||||
if (is_sep_native_compat(string[len - 1])) {
|
||||
string[len - 1] = '\0';
|
||||
len--;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace blender::editor::animation::tests {
|
|||
const float KEYLIST_NEAR_ERROR = 0.1;
|
||||
const float FRAME_STEP = 0.005;
|
||||
|
||||
/* Build FCurve with keys on frames 10, 20, and 30. */
|
||||
static void build_fcurve(FCurve &fcurve)
|
||||
{
|
||||
fcurve.totvert = 3;
|
||||
|
@ -51,92 +52,88 @@ static void assert_act_key_column(const ActKeyColumn *column,
|
|||
const std::optional<float> expected_frame)
|
||||
{
|
||||
if (expected_frame.has_value()) {
|
||||
EXPECT_NE(column, nullptr);
|
||||
ASSERT_NE(column, nullptr) << "Expected a frame to be found at " << *expected_frame;
|
||||
EXPECT_NEAR(column->cfra, *expected_frame, KEYLIST_NEAR_ERROR);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(column, nullptr);
|
||||
EXPECT_EQ(column, nullptr) << "Expected no frame to be found, but found " << column->cfra;
|
||||
}
|
||||
}
|
||||
|
||||
using KeylistFindFunction = std::function<const ActKeyColumn *(const AnimKeylist *, float)>;
|
||||
|
||||
static float check_keylist_find_range(const AnimKeylist *keylist,
|
||||
KeylistFindFunction keylist_find_func,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
static void check_keylist_find_range(const AnimKeylist *keylist,
|
||||
KeylistFindFunction keylist_find_func,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
{
|
||||
float cfra = frame_from;
|
||||
for (; cfra < frame_to; cfra += FRAME_STEP) {
|
||||
const ActKeyColumn *found = keylist_find_func(keylist, cfra);
|
||||
assert_act_key_column(found, expected_frame);
|
||||
}
|
||||
return cfra;
|
||||
}
|
||||
|
||||
static float check_keylist_find_next_range(const AnimKeylist *keylist,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
static void check_keylist_find_next_range(const AnimKeylist *keylist,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
{
|
||||
return check_keylist_find_range(
|
||||
keylist, ED_keylist_find_next, frame_from, frame_to, expected_frame);
|
||||
check_keylist_find_range(keylist, ED_keylist_find_next, frame_from, frame_to, expected_frame);
|
||||
}
|
||||
|
||||
TEST(keylist, find_next)
|
||||
{
|
||||
AnimKeylist *keylist = create_test_keylist();
|
||||
|
||||
float cfra = check_keylist_find_next_range(keylist, 0.0f, 9.99f, 10.0f);
|
||||
cfra = check_keylist_find_next_range(keylist, cfra, 19.99f, 20.0f);
|
||||
cfra = check_keylist_find_next_range(keylist, cfra, 29.99f, 30.0f);
|
||||
cfra = check_keylist_find_next_range(keylist, cfra, 39.99f, std::nullopt);
|
||||
check_keylist_find_next_range(keylist, 0.0f, 9.99f, 10.0f);
|
||||
check_keylist_find_next_range(keylist, 10.0f, 19.99f, 20.0f);
|
||||
check_keylist_find_next_range(keylist, 20.0f, 29.99f, 30.0f);
|
||||
check_keylist_find_next_range(keylist, 30.0f, 39.99f, std::nullopt);
|
||||
|
||||
ED_keylist_free(keylist);
|
||||
}
|
||||
|
||||
static float check_keylist_find_prev_range(const AnimKeylist *keylist,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
static void check_keylist_find_prev_range(const AnimKeylist *keylist,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
{
|
||||
return check_keylist_find_range(
|
||||
keylist, ED_keylist_find_prev, frame_from, frame_to, expected_frame);
|
||||
check_keylist_find_range(keylist, ED_keylist_find_prev, frame_from, frame_to, expected_frame);
|
||||
}
|
||||
|
||||
TEST(keylist, find_prev)
|
||||
{
|
||||
AnimKeylist *keylist = create_test_keylist();
|
||||
|
||||
float cfra = check_keylist_find_prev_range(keylist, 0.0f, 10.01f, std::nullopt);
|
||||
cfra = check_keylist_find_prev_range(keylist, cfra, 20.01f, 10.0f);
|
||||
cfra = check_keylist_find_prev_range(keylist, cfra, 30.01f, 20.0f);
|
||||
cfra = check_keylist_find_prev_range(keylist, cfra, 49.99f, 30.0f);
|
||||
check_keylist_find_prev_range(keylist, 0.0f, 10.00f, std::nullopt);
|
||||
check_keylist_find_prev_range(keylist, 10.01f, 20.00f, 10.0f);
|
||||
check_keylist_find_prev_range(keylist, 20.01f, 30.00f, 20.0f);
|
||||
check_keylist_find_prev_range(keylist, 30.01f, 49.99f, 30.0f);
|
||||
|
||||
ED_keylist_free(keylist);
|
||||
}
|
||||
|
||||
static float check_keylist_find_exact_range(const AnimKeylist *keylist,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
static void check_keylist_find_exact_range(const AnimKeylist *keylist,
|
||||
const float frame_from,
|
||||
const float frame_to,
|
||||
const std::optional<float> expected_frame)
|
||||
{
|
||||
return check_keylist_find_range(
|
||||
keylist, ED_keylist_find_exact, frame_from, frame_to, expected_frame);
|
||||
check_keylist_find_range(keylist, ED_keylist_find_exact, frame_from, frame_to, expected_frame);
|
||||
}
|
||||
|
||||
TEST(keylist, find_exact)
|
||||
{
|
||||
AnimKeylist *keylist = create_test_keylist();
|
||||
|
||||
float cfra = check_keylist_find_exact_range(keylist, 0.0f, 9.99f, std::nullopt);
|
||||
cfra = check_keylist_find_exact_range(keylist, cfra, 10.01f, 10.0f);
|
||||
cfra = check_keylist_find_exact_range(keylist, cfra, 19.99f, std::nullopt);
|
||||
cfra = check_keylist_find_exact_range(keylist, cfra, 20.01f, 20.0f);
|
||||
cfra = check_keylist_find_exact_range(keylist, cfra, 29.99f, std::nullopt);
|
||||
cfra = check_keylist_find_exact_range(keylist, cfra, 30.01f, 30.0f);
|
||||
cfra = check_keylist_find_exact_range(keylist, cfra, 49.99f, std::nullopt);
|
||||
check_keylist_find_exact_range(keylist, 0.0f, 9.99f, std::nullopt);
|
||||
check_keylist_find_exact_range(keylist, 9.9901f, 10.01f, 10.0f);
|
||||
check_keylist_find_exact_range(keylist, 10.01f, 19.99f, std::nullopt);
|
||||
check_keylist_find_exact_range(keylist, 19.9901f, 20.01f, 20.0f);
|
||||
check_keylist_find_exact_range(keylist, 20.01f, 29.99f, std::nullopt);
|
||||
check_keylist_find_exact_range(keylist, 29.9901f, 30.01f, 30.0f);
|
||||
check_keylist_find_exact_range(keylist, 30.01f, 49.99f, std::nullopt);
|
||||
|
||||
ED_keylist_free(keylist);
|
||||
}
|
||||
|
|
|
@ -5849,7 +5849,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot)
|
|||
ot->ui = sculpt_redo_empty_ui;
|
||||
|
||||
/* Flags (sculpt does own undo? (ton)). */
|
||||
ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
ot->flag = OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties. */
|
||||
|
||||
|
|
|
@ -910,13 +910,15 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
|
|||
v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR;
|
||||
}
|
||||
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
|
||||
|
||||
/* Color data is not available in multi-resolution or dynamic topology. */
|
||||
if (!SCULPT_handles_colors_report(ss, op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob);
|
||||
BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd);
|
||||
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
|
||||
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
|
||||
|
|
|
@ -106,16 +106,22 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
|
||||
/* Note this does not remove auto-keys on locked cameras. */
|
||||
copy_qt_qt(vod->rv3d->viewquat, vod->init.quat);
|
||||
ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
|
||||
viewops_data_free(C, op->customdata);
|
||||
op->customdata = NULL;
|
||||
return OPERATOR_CANCELLED;
|
||||
else if (event->type == vod->init.event_type) {
|
||||
/* Check `vod->init.event_type` first in case RMB was used to invoke.
|
||||
* in this case confirming takes precedence over canceling, see: T102937. */
|
||||
if (event->val == KM_RELEASE) {
|
||||
event_code = VIEW_CONFIRM;
|
||||
}
|
||||
}
|
||||
else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
|
||||
event_code = VIEW_CONFIRM;
|
||||
else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
|
||||
if (event->val == KM_PRESS) {
|
||||
/* Note this does not remove auto-keys on locked cameras. */
|
||||
copy_qt_qt(vod->rv3d->viewquat, vod->init.quat);
|
||||
ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
|
||||
viewops_data_free(C, op->customdata);
|
||||
op->customdata = NULL;
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (event_code == VIEW_APPLY) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_boxpack_2d.h"
|
||||
#include "BLI_convexhull_2d.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
@ -16,6 +17,7 @@
|
|||
#include "BLI_polyfill_2d.h"
|
||||
#include "BLI_polyfill_2d_beautify.h"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "eigen_capi.h"
|
||||
|
||||
|
@ -1122,32 +1124,6 @@ static PFace *p_face_add_fill(ParamHandle *handle, PChart *chart, PVert *v1, PVe
|
|||
return f;
|
||||
}
|
||||
|
||||
static bool p_quad_split_direction(ParamHandle *handle, const float **co, const ParamKey *vkeys)
|
||||
{
|
||||
/* Slight bias to prefer one edge over the other in case they are equal, so
|
||||
* that in symmetric models we choose the same split direction instead of
|
||||
* depending on floating point errors to decide. */
|
||||
float bias = 1.0f + 1e-6f;
|
||||
float fac = len_v3v3(co[0], co[2]) * bias - len_v3v3(co[1], co[3]);
|
||||
bool dir = (fac <= 0.0f);
|
||||
|
||||
/* The face exists check is there because of a special case:
|
||||
* when two quads share three vertices, they can each be split into two triangles,
|
||||
* resulting in two identical triangles. For example in Suzanne's nose. */
|
||||
if (dir) {
|
||||
if (p_face_exists(handle, vkeys, 0, 1, 2) || p_face_exists(handle, vkeys, 0, 2, 3)) {
|
||||
return !dir;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (p_face_exists(handle, vkeys, 0, 1, 3) || p_face_exists(handle, vkeys, 1, 2, 3)) {
|
||||
return !dir;
|
||||
}
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/* Construction: boundary filling */
|
||||
|
||||
static void p_chart_boundaries(PChart *chart, PEdge **r_outer)
|
||||
|
@ -3895,21 +3871,75 @@ void GEO_uv_parametrizer_face_add(ParamHandle *phandle,
|
|||
BLI_assert(nverts >= 3);
|
||||
param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
|
||||
|
||||
if (nverts > 4) {
|
||||
if (nverts > 3) {
|
||||
/* Protect against (manifold) geometry which has a non-manifold triangulation.
|
||||
* See T102543. */
|
||||
|
||||
blender::Vector<int, 32> permute;
|
||||
permute.reserve(nverts);
|
||||
for (int i = 0; i < nverts; i++) {
|
||||
permute.append_unchecked(i);
|
||||
}
|
||||
|
||||
int i = nverts - 1;
|
||||
while (i >= 0) {
|
||||
/* Just check the "ears" of the n-gon.
|
||||
* For quads, this is sufficient.
|
||||
* For pents and higher, we might miss internal duplicate triangles, but note
|
||||
* that such cases are rare if the source geometry is manifold and non-intersecting. */
|
||||
int pm = permute.size();
|
||||
BLI_assert(pm > 3);
|
||||
int i0 = permute[i];
|
||||
int i1 = permute[(i + 1) % pm];
|
||||
int i2 = permute[(i + 2) % pm];
|
||||
if (!p_face_exists(phandle, vkeys, i0, i1, i2)) {
|
||||
i--; /* ...All good...*/
|
||||
continue;
|
||||
}
|
||||
|
||||
/* An existing triangle has already been inserted. As a heuristic, attempt to add the
|
||||
* *previous* triangle. \note: Should probably call `GEO_uv_parametrizer_face_add` instead of
|
||||
* `p_face_add_construct`. */
|
||||
int iprev = permute[(i + pm - 1) % pm];
|
||||
p_face_add_construct(phandle, key, vkeys, co, uv, iprev, i0, i1, pin, select);
|
||||
|
||||
permute.remove(i);
|
||||
if (permute.size() == 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (permute.size() != nverts) {
|
||||
int pm = permute.size();
|
||||
/* Add the remaining pm-gon. */
|
||||
blender::Array<ParamKey> vkeys_sub(pm);
|
||||
blender::Array<const float *> co_sub(pm);
|
||||
blender::Array<float *> uv_sub(pm);
|
||||
blender::Array<bool> pin_sub(pm);
|
||||
blender::Array<bool> select_sub(pm);
|
||||
for (int i = 0; i < pm; i++) {
|
||||
int j = permute[i];
|
||||
vkeys_sub[i] = vkeys[j];
|
||||
co_sub[i] = co[j];
|
||||
uv_sub[i] = uv[j];
|
||||
pin_sub[i] = pin && pin[j];
|
||||
select_sub[i] = select && select[j];
|
||||
}
|
||||
p_add_ngon(phandle,
|
||||
key,
|
||||
pm,
|
||||
&vkeys_sub.first(),
|
||||
&co_sub.first(),
|
||||
&uv_sub.first(),
|
||||
&pin_sub.first(),
|
||||
&select_sub.first());
|
||||
return; /* Nothing more to do. */
|
||||
}
|
||||
/* No "ears" have previously been inserted. Continue as normal. */
|
||||
}
|
||||
if (nverts > 3) {
|
||||
/* ngon */
|
||||
p_add_ngon(phandle, key, nverts, vkeys, co, uv, pin, select);
|
||||
}
|
||||
else if (nverts == 4) {
|
||||
/* quad */
|
||||
if (p_quad_split_direction(phandle, co, vkeys)) {
|
||||
p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 2, pin, select);
|
||||
p_face_add_construct(phandle, key, vkeys, co, uv, 0, 2, 3, pin, select);
|
||||
}
|
||||
else {
|
||||
p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 3, pin, select);
|
||||
p_face_add_construct(phandle, key, vkeys, co, uv, 1, 2, 3, pin, select);
|
||||
}
|
||||
}
|
||||
else if (!p_face_exists(phandle, vkeys, 0, 1, 2)) {
|
||||
/* triangle */
|
||||
p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 2, pin, select);
|
||||
|
|
|
@ -2215,8 +2215,8 @@ struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath,
|
|||
|
||||
float scale_factor = MIN2(float(max_thumb_size) / float(source_w),
|
||||
float(max_thumb_size) / float(source_h));
|
||||
int dest_w = int(source_w * scale_factor);
|
||||
int dest_h = int(source_h * scale_factor);
|
||||
int dest_w = MAX2(int(source_w * scale_factor), 1);
|
||||
int dest_h = MAX2(int(source_h * scale_factor), 1);
|
||||
|
||||
struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rectfloat);
|
||||
|
||||
|
|
|
@ -112,8 +112,8 @@ struct ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath,
|
|||
*r_height = (size_t)config.input.height;
|
||||
|
||||
const float scale = (float)max_thumb_size / MAX2(config.input.width, config.input.height);
|
||||
const int dest_w = (int)(config.input.width * scale);
|
||||
const int dest_h = (int)(config.input.height * scale);
|
||||
const int dest_w = MAX2((int)(config.input.width * scale), 1);
|
||||
const int dest_h = MAX2((int)(config.input.height * scale), 1);
|
||||
|
||||
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
|
||||
struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rect);
|
||||
|
|
|
@ -1033,6 +1033,7 @@ static void rna_def_tex_slot(BlenderRNA *brna)
|
|||
RNA_def_property_string_funcs(
|
||||
prop, "rna_TexPaintSlot_name_get", "rna_TexPaintSlot_name_length", NULL);
|
||||
RNA_def_property_ui_text(prop, "Name", "Name of the slot");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "icon_value", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
|
|
@ -1052,10 +1052,10 @@ static void shader_node_disconnect_inactive_mix_branch(bNodeTree *ntree,
|
|||
static void ntree_shader_disconnect_inactive_mix_branches(bNodeTree *ntree)
|
||||
{
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
if (node->type == SH_NODE_MIX_SHADER) {
|
||||
if (node->typeinfo->type == SH_NODE_MIX_SHADER) {
|
||||
shader_node_disconnect_inactive_mix_branch(ntree, node, 0, 1, 2, true);
|
||||
}
|
||||
else if (node->type == SH_NODE_MIX) {
|
||||
else if (node->typeinfo->type == SH_NODE_MIX) {
|
||||
const NodeShaderMix *storage = static_cast<NodeShaderMix *>(node->storage);
|
||||
if (storage->data_type == SOCK_FLOAT) {
|
||||
shader_node_disconnect_inactive_mix_branch(ntree, node, 0, 2, 3, storage->clamp_factor);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
#include "BKE_undo_system.h"
|
||||
#include "BKE_workspace.h"
|
||||
|
||||
#include "BKE_sound.h"
|
||||
|
@ -928,6 +929,17 @@ void WM_reportf(eReportType type, const char *format, ...)
|
|||
/** \name Operator Logic
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Return the active undo step as an identifier for the purpose of comparison only.
|
||||
*/
|
||||
static intptr_t wm_operator_undo_active_id(const wmWindowManager *wm)
|
||||
{
|
||||
if (wm->undo_stack) {
|
||||
return intptr_t(wm->undo_stack->step_active);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool WM_operator_poll(bContext *C, wmOperatorType *ot)
|
||||
{
|
||||
|
||||
|
@ -1059,7 +1071,12 @@ static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
|
|||
return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
|
||||
}
|
||||
|
||||
static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store)
|
||||
/**
|
||||
* \param has_undo_step: True when an undo step was added,
|
||||
* needed when the operator doesn't use #OPTYPE_UNDO, #OPTYPE_UNDO_GROUPED but adds an undo step.
|
||||
*/
|
||||
static void wm_operator_finished(
|
||||
bContext *C, wmOperator *op, const bool repeat, const bool store, const bool has_undo_step)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
enum {
|
||||
|
@ -1090,6 +1107,11 @@ static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat,
|
|||
hud_status = CLEAR;
|
||||
}
|
||||
}
|
||||
else if (has_undo_step) {
|
||||
if (repeat == 0) {
|
||||
hud_status = CLEAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (repeat == 0) {
|
||||
|
@ -1150,6 +1172,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
|
|||
return retval;
|
||||
}
|
||||
|
||||
const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
|
||||
if (op->type->exec) {
|
||||
if (op->type->flag & OPTYPE_UNDO) {
|
||||
wm->op_undo_depth++;
|
||||
|
@ -1171,7 +1194,9 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
|
|||
}
|
||||
|
||||
if (retval & OPERATOR_FINISHED) {
|
||||
wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
|
||||
const bool has_undo_step = (undo_id_prev != wm_operator_undo_active_id(wm));
|
||||
|
||||
wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0, has_undo_step);
|
||||
}
|
||||
else if (repeat == 0) {
|
||||
/* WARNING: modal from exec is bad practice, but avoid crashing. */
|
||||
|
@ -1412,6 +1437,7 @@ static int wm_operator_invoke(bContext *C,
|
|||
|
||||
if (WM_operator_poll(C, ot)) {
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
|
||||
|
||||
/* If `reports == nullptr`, they'll be initialized. */
|
||||
wmOperator *op = wm_operator_create(wm, ot, properties, reports);
|
||||
|
@ -1480,8 +1506,9 @@ static int wm_operator_invoke(bContext *C,
|
|||
/* Do nothing, #wm_operator_exec() has been called somewhere. */
|
||||
}
|
||||
else if (retval & OPERATOR_FINISHED) {
|
||||
const bool has_undo_step = (undo_id_prev != wm_operator_undo_active_id(wm));
|
||||
const bool store = !is_nested_call && use_last_properties;
|
||||
wm_operator_finished(C, op, false, store);
|
||||
wm_operator_finished(C, op, false, store, has_undo_step);
|
||||
}
|
||||
else if (retval & OPERATOR_RUNNING_MODAL) {
|
||||
/* Take ownership of reports (in case python provided own). */
|
||||
|
@ -2378,6 +2405,7 @@ static int wm_handler_operator_call(bContext *C,
|
|||
wmEvent_ModalMapStore event_backup;
|
||||
wm_event_modalkeymap_begin(C, op, event, &event_backup);
|
||||
|
||||
const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
|
||||
if (ot->flag & OPTYPE_UNDO) {
|
||||
wm->op_undo_depth++;
|
||||
}
|
||||
|
@ -2413,7 +2441,9 @@ static int wm_handler_operator_call(bContext *C,
|
|||
|
||||
/* Important to run 'wm_operator_finished' before setting the context members to null. */
|
||||
if (retval & OPERATOR_FINISHED) {
|
||||
wm_operator_finished(C, op, false, true);
|
||||
const bool has_undo_step = (undo_id_prev != wm_operator_undo_active_id(wm));
|
||||
|
||||
wm_operator_finished(C, op, false, true, has_undo_step);
|
||||
handler->op = nullptr;
|
||||
}
|
||||
else if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
|
||||
|
|
Loading…
Reference in New Issue