diff options
author | Omar Emara <mail@OmarEmara.dev> | 2022-06-15 15:35:44 +0300 |
---|---|---|
committer | Omar Emara <mail@OmarEmara.dev> | 2022-06-15 15:35:44 +0300 |
commit | 082c87bf9624e888edc40c27a9b9fcaea32a6195 (patch) | |
tree | c6d67d8a5c4fdcca7ee34a61329f58483a8eb8e5 | |
parent | 034cea15470ac161f9f5d047c07bb7fb22980e3d (diff) | |
parent | 2e6cd7047397b0724b5fbafa775d0b02bfd0c329 (diff) |
Merge branch 'master' into temp-viewport-compositor-merge
64 files changed, 1760 insertions, 1218 deletions
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 0a6e19f006c..5b06b5d98e6 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -324,16 +324,21 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ${CMAKE_CURRENT_BINARY_DIR} ) - # xdg-shell. + # `xdg-shell`. generate_protocol_bindings( xdg-shell "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" ) - # xdg-decoration. + # `xdg-decoration`. generate_protocol_bindings( xdg-decoration "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" ) + # `xdg-output`. + generate_protocol_bindings( + xdg-output + "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-output/xdg-output-unstable-v1.xml" + ) # Pointer-constraints. generate_protocol_bindings( pointer-constraints diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 9e683ceb437..b59adef1724 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -31,6 +31,8 @@ #include <relative-pointer-client-protocol.h> #include <tablet-client-protocol.h> #include <wayland-cursor.h> +#include <xdg-output-client-protocol.h> + #include <xkbcommon/xkbcommon.h> #include <fcntl.h> @@ -40,7 +42,7 @@ #include <cstring> #include <mutex> -static GHOST_IWindow *get_window(struct wl_surface *surface); +static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface); /* -------------------------------------------------------------------- */ /** \name Private Types & Defines @@ -70,18 +72,18 @@ static GHOST_IWindow *get_window(struct wl_surface *surface); #define BTN_STYLUS3 0x149 struct buffer_t { - void *data; - size_t size; + void *data = nullptr; + size_t size = 0; }; struct cursor_t { - bool visible; - struct wl_surface *surface = nullptr; - struct wl_buffer *buffer; - struct wl_cursor_image image; + bool visible = false; + struct wl_surface *wl_surface = nullptr; + struct wl_buffer *wl_buffer = nullptr; + struct wl_cursor_image wl_image = {0}; + struct wl_cursor_theme *wl_theme = nullptr; struct buffer_t *file_buffer = nullptr; - struct wl_cursor_theme *theme = nullptr; - int size; + int size = 0; std::string theme_name; /** Outputs on which the cursor is visible. */ std::unordered_set<const output_t *> outputs; @@ -94,53 +96,52 @@ struct cursor_t { * Since are no API's to access properties of the tool, store the values here. */ struct tablet_tool_input_t { - struct input_t *input; - struct wl_surface *cursor_surface; + struct input_t *input = nullptr; + struct wl_surface *cursor_surface = nullptr; - GHOST_TabletData data; + GHOST_TabletData data = GHOST_TABLET_DATA_NONE; }; struct data_offer_t { std::unordered_set<std::string> types; - uint32_t source_actions; - uint32_t dnd_action; - struct wl_data_offer *id; - std::atomic<bool> in_use; + uint32_t source_actions = 0; + uint32_t dnd_action = 0; + struct wl_data_offer *id = nullptr; + std::atomic<bool> in_use = false; struct { /** Compatible with #input_t.xy coordinates. */ - wl_fixed_t xy[2]; + wl_fixed_t xy[2] = {0, 0}; } dnd; }; struct data_source_t { - struct wl_data_source *data_source; - char *buffer_out; + struct wl_data_source *data_source = nullptr; + char *buffer_out = nullptr; }; struct key_repeat_payload_t { - GHOST_SystemWayland *system; - GHOST_IWindow *window; - GHOST_TKey key; - GHOST_TEventKeyData key_data; + GHOST_SystemWayland *system = nullptr; + GHOST_IWindow *window = nullptr; + GHOST_TEventKeyData key_data = {GHOST_kKeyUnknown}; }; struct input_t { - GHOST_SystemWayland *system; + GHOST_SystemWayland *system = nullptr; std::string name; - struct wl_seat *seat; - struct wl_pointer *pointer = nullptr; - struct wl_keyboard *keyboard = nullptr; + struct wl_seat *wl_seat = nullptr; + struct wl_pointer *wl_pointer = nullptr; + struct wl_keyboard *wl_keyboard = nullptr; struct zwp_tablet_seat_v2 *tablet_seat = nullptr; /** All currently active tablet tools (needed for changing the cursor). */ std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools; - uint32_t pointer_serial; - uint32_t tablet_serial; + uint32_t pointer_serial = 0; + uint32_t tablet_serial = 0; /** Use to check if the last cursor input was tablet or pointer. */ - uint32_t cursor_serial; + uint32_t cursor_serial = 0; /** * High precision mouse coordinates (pointer or tablet). @@ -154,21 +155,21 @@ struct input_t { * }; * \endocde */ - wl_fixed_t xy[2]; - GHOST_Buttons buttons; + wl_fixed_t xy[2] = {0, 0}; + GHOST_Buttons buttons = GHOST_Buttons(); struct cursor_t cursor; - struct zwp_relative_pointer_v1 *relative_pointer; - struct zwp_locked_pointer_v1 *locked_pointer; - struct zwp_confined_pointer_v1 *confined_pointer; + struct zwp_relative_pointer_v1 *relative_pointer = nullptr; + struct zwp_locked_pointer_v1 *locked_pointer = nullptr; + struct zwp_confined_pointer_v1 *confined_pointer = nullptr; - struct xkb_context *xkb_context; - struct xkb_state *xkb_state; + struct xkb_context *xkb_context = nullptr; + struct xkb_state *xkb_state = nullptr; struct { /** Key repetition in character per second. */ - int32_t rate; + int32_t rate = 0; /** Time (milliseconds) after which to start repeating keys. */ - int32_t delay; + int32_t delay = 0; /** Timer for key repeats. */ GHOST_ITimerTask *timer = nullptr; } key_repeat; @@ -180,33 +181,34 @@ struct input_t { struct wl_data_device *data_device = nullptr; /** Drag & Drop. */ - struct data_offer_t *data_offer_dnd; + struct data_offer_t *data_offer_dnd = nullptr; std::mutex data_offer_dnd_mutex; /** Copy & Paste. */ - struct data_offer_t *data_offer_copy_paste; + struct data_offer_t *data_offer_copy_paste = nullptr; std::mutex data_offer_copy_paste_mutex; - struct data_source_t *data_source; + struct data_source_t *data_source = nullptr; std::mutex data_source_mutex; /** Last device that was active. */ - uint32_t data_source_serial; + uint32_t data_source_serial = 0; }; struct display_t { - GHOST_SystemWayland *system; + GHOST_SystemWayland *system = nullptr; - struct wl_display *display; + struct wl_display *display = nullptr; struct wl_compositor *compositor = nullptr; struct xdg_wm_base *xdg_shell = nullptr; struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr; + struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; struct wl_shm *shm = nullptr; std::vector<output_t *> outputs; std::vector<input_t *> inputs; struct { std::string theme; - int size; + int size = 0; } cursor; struct wl_data_device_manager *data_device_manager = nullptr; struct zwp_tablet_manager_v2 *tablet_manager = nullptr; @@ -256,7 +258,7 @@ static void display_destroy(display_t *d) } for (output_t *output : d->outputs) { - wl_output_destroy(output->output); + wl_output_destroy(output->wl_output); delete output; } @@ -275,28 +277,28 @@ static void display_destroy(display_t *d) if (input->data_device) { wl_data_device_release(input->data_device); } - if (input->pointer) { + if (input->wl_pointer) { if (input->cursor.file_buffer) { munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); delete input->cursor.file_buffer; } - if (input->cursor.surface) { - wl_surface_destroy(input->cursor.surface); + if (input->cursor.wl_surface) { + wl_surface_destroy(input->cursor.wl_surface); } - if (input->cursor.theme) { - wl_cursor_theme_destroy(input->cursor.theme); + if (input->cursor.wl_theme) { + wl_cursor_theme_destroy(input->cursor.wl_theme); } - if (input->pointer) { - wl_pointer_destroy(input->pointer); + if (input->wl_pointer) { + wl_pointer_destroy(input->wl_pointer); } } - if (input->keyboard) { + if (input->wl_keyboard) { if (input->key_repeat.timer) { delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData()); input->system->removeTimer(input->key_repeat.timer); input->key_repeat.timer = nullptr; } - wl_keyboard_destroy(input->keyboard); + wl_keyboard_destroy(input->wl_keyboard); } if (input->xkb_state) { xkb_state_unref(input->xkb_state); @@ -304,7 +306,7 @@ static void display_destroy(display_t *d) if (input->xkb_context) { xkb_context_unref(input->xkb_context); } - wl_seat_destroy(input->seat); + wl_seat_destroy(input->wl_seat); delete input; } @@ -550,7 +552,7 @@ static const std::vector<std::string> mime_send = { * an event is received from the compositor. * \{ */ -static void relative_pointer_relative_motion( +static void relative_pointer_handle_relative_motion( void *data, struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/, uint32_t /*utime_hi*/, @@ -561,8 +563,8 @@ static void relative_pointer_relative_motion( wl_fixed_t /*dy_unaccel*/) { input_t *input = static_cast<input_t *>(data); - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_pointer)); - if (win == nullptr) { + GHOST_WindowWayland *win = window_from_surface(input->focus_pointer); + if (!win) { return; } const wl_fixed_t scale = win->scale(); @@ -578,7 +580,7 @@ static void relative_pointer_relative_motion( } static const zwp_relative_pointer_v1_listener relative_pointer_listener = { - relative_pointer_relative_motion, + relative_pointer_handle_relative_motion, }; /** \} */ @@ -643,17 +645,17 @@ static std::string read_pipe(data_offer_t *data_offer, * Sent when a target accepts pointer_focus or motion events. If * a target does not accept any of the offered types, type is nullptr. */ -static void data_source_target(void * /*data*/, - struct wl_data_source * /*wl_data_source*/, - const char * /*mime_type*/) +static void data_source_handle_target(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/) { /* pass */ } -static void data_source_send(void *data, - struct wl_data_source * /*wl_data_source*/, - const char * /*mime_type*/, - int32_t fd) +static void data_source_handle_send(void *data, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/, + int32_t fd) { input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_source_mutex}; @@ -665,7 +667,7 @@ static void data_source_send(void *data, close(fd); } -static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_data_source) +static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source *wl_data_source) { wl_data_source_destroy(wl_data_source); } @@ -677,8 +679,8 @@ static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_dat * indicate acceptance, #wl_data_source.cancelled may still be * emitted afterwards if the drop destination does not accept any mime type. */ -static void data_source_dnd_drop_performed(void * /*data*/, - struct wl_data_source * /*wl_data_source*/) +static void data_source_handle_dnd_drop_performed(void * /*data*/, + struct wl_data_source * /*wl_data_source*/) { /* pass */ } @@ -690,7 +692,8 @@ static void data_source_dnd_drop_performed(void * /*data*/, * source, so the client is now free to destroy this data source * and free all associated data. */ -static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /*wl_data_source*/) +static void data_source_handle_dnd_finished(void * /*data*/, + struct wl_data_source * /*wl_data_source*/) { /* pass */ } @@ -702,20 +705,20 @@ static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /* * after matching the source/destination side actions. Only one * action (or none) will be offered here. */ -static void data_source_action(void * /*data*/, - struct wl_data_source * /*wl_data_source*/, - uint32_t /*dnd_action*/) +static void data_source_handle_action(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + uint32_t /*dnd_action*/) { /* pass */ } static const struct wl_data_source_listener data_source_listener = { - data_source_target, - data_source_send, - data_source_cancelled, - data_source_dnd_drop_performed, - data_source_dnd_finished, - data_source_action, + data_source_handle_target, + data_source_handle_send, + data_source_handle_cancelled, + data_source_handle_dnd_drop_performed, + data_source_handle_dnd_finished, + data_source_handle_action, }; /** \} */ @@ -724,31 +727,31 @@ static const struct wl_data_source_listener data_source_listener = { /** \name Listener (Data Offer), #wl_data_offer_listener * \{ */ -static void data_offer_offer(void *data, - struct wl_data_offer * /*wl_data_offer*/, - const char *mime_type) +static void data_offer_handle_offer(void *data, + struct wl_data_offer * /*wl_data_offer*/, + const char *mime_type) { static_cast<data_offer_t *>(data)->types.insert(mime_type); } -static void data_offer_source_actions(void *data, - struct wl_data_offer * /*wl_data_offer*/, - uint32_t source_actions) +static void data_offer_handle_source_actions(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t source_actions) { static_cast<data_offer_t *>(data)->source_actions = source_actions; } -static void data_offer_action(void *data, - struct wl_data_offer * /*wl_data_offer*/, - uint32_t dnd_action) +static void data_offer_handle_action(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t dnd_action) { static_cast<data_offer_t *>(data)->dnd_action = dnd_action; } static const struct wl_data_offer_listener data_offer_listener = { - data_offer_offer, - data_offer_source_actions, - data_offer_action, + data_offer_handle_offer, + data_offer_handle_source_actions, + data_offer_handle_action, }; /** \} */ @@ -757,22 +760,22 @@ static const struct wl_data_offer_listener data_offer_listener = { /** \name Listener (Data Device), #wl_data_device_listener * \{ */ -static void data_device_data_offer(void * /*data*/, - struct wl_data_device * /*wl_data_device*/, - struct wl_data_offer *id) +static void data_device_handle_data_offer(void * /*data*/, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) { data_offer_t *data_offer = new data_offer_t; data_offer->id = id; wl_data_offer_add_listener(id, &data_offer_listener, data_offer); } -static void data_device_enter(void *data, - struct wl_data_device * /*wl_data_device*/, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t x, - wl_fixed_t y, - struct wl_data_offer *id) +static void data_device_handle_enter(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) { input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; @@ -797,7 +800,7 @@ static void data_device_enter(void *data, dnd_events(input, GHOST_kEventDraggingEntered); } -static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/) +static void data_device_handle_leave(void *data, struct wl_data_device * /*wl_data_device*/) { input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; @@ -812,11 +815,11 @@ static void data_device_leave(void *data, struct wl_data_device * /*wl_data_devi } } -static void data_device_motion(void *data, - struct wl_data_device * /*wl_data_device*/, - uint32_t /*time*/, - wl_fixed_t x, - wl_fixed_t y) +static void data_device_handle_motion(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t /*time*/, + wl_fixed_t x, + wl_fixed_t y) { input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; @@ -827,7 +830,7 @@ static void data_device_motion(void *data, dnd_events(input, GHOST_kEventDraggingUpdated); } -static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/) +static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_data_device*/) { input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; @@ -839,11 +842,11 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic data_offer->types.begin(), data_offer->types.end()); - auto read_uris = [](input_t *const input, - data_offer_t *data_offer, - wl_surface *surface, - const std::string mime_receive) { - const wl_fixed_t *xy = data_offer->dnd.xy; + auto read_uris_fn = [](input_t *const input, + data_offer_t *data_offer, + wl_surface *surface, + const std::string mime_receive) { + const wl_fixed_t xy[2] = {data_offer->dnd.xy[0], data_offer->dnd.xy[1]}; const std::string data = read_pipe(data_offer, mime_receive, nullptr); @@ -859,7 +862,7 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic static constexpr const char *file_proto = "file://"; static constexpr const char *crlf = "\r\n"; - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface)); + GHOST_WindowWayland *win = window_from_surface(surface); GHOST_ASSERT(win != nullptr, "Unable to find window for drop event from surface"); std::vector<std::string> uris; @@ -904,13 +907,13 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback * (#data_device_leave) will clear the value once this function starts. */ - std::thread read_thread(read_uris, input, data_offer, input->focus_dnd, mime_receive); + std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive); read_thread.detach(); } -static void data_device_selection(void *data, - struct wl_data_device * /*wl_data_device*/, - struct wl_data_offer *id) +static void data_device_handle_selection(void *data, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) { input_t *input = static_cast<input_t *>(data); @@ -933,7 +936,7 @@ static void data_device_selection(void *data, data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); input->data_offer_copy_paste = data_offer; - auto read_selection = [](input_t *input) { + auto read_selection_fn = [](input_t *input) { GHOST_SystemWayland *const system = input->system; input->data_offer_copy_paste_mutex.lock(); @@ -954,50 +957,55 @@ static void data_device_selection(void *data, } }; - std::thread read_thread(read_selection, input); + std::thread read_thread(read_selection_fn, input); read_thread.detach(); } static const struct wl_data_device_listener data_device_listener = { - data_device_data_offer, - data_device_enter, - data_device_leave, - data_device_motion, - data_device_drop, - data_device_selection, + data_device_handle_data_offer, + data_device_handle_enter, + data_device_handle_leave, + data_device_handle_motion, + data_device_handle_drop, + data_device_handle_selection, }; /** \} */ /* -------------------------------------------------------------------- */ -/** \name Listener (Surface), #wl_surface_listener +/** \name Listener (Buffer), #wl_buffer_listener * \{ */ -static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer) +static void cursor_buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { cursor_t *cursor = static_cast<cursor_t *>(data); wl_buffer_destroy(wl_buffer); - if (wl_buffer == cursor->buffer) { + if (wl_buffer == cursor->wl_buffer) { /* the mapped buffer was from a custom cursor */ - cursor->buffer = nullptr; + cursor->wl_buffer = nullptr; } } const struct wl_buffer_listener cursor_buffer_listener = { - cursor_buffer_release, + cursor_buffer_handle_release, }; -static GHOST_IWindow *get_window(struct wl_surface *surface) -{ - if (!surface) { - return nullptr; - } +/** \} */ - for (GHOST_IWindow *win : window_manager->getWindows()) { - if (surface == static_cast<const GHOST_WindowWayland *>(win)->surface()) { - return win; +/* -------------------------------------------------------------------- */ +/** \name Listener (Surface), #wl_surface_listener + * \{ */ + +static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface) +{ + if (surface) { + for (GHOST_IWindow *iwin : window_manager->getWindows()) { + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin); + if (surface == win->surface()) { + return win; + } } } return nullptr; @@ -1014,34 +1022,34 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) if (scale > 0 && cursor.scale != scale) { cursor.scale = scale; - wl_surface_set_buffer_scale(cursor.surface, scale); - wl_cursor_theme_destroy(cursor.theme); - cursor.theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); + wl_surface_set_buffer_scale(cursor.wl_surface, scale); + wl_cursor_theme_destroy(cursor.wl_theme); + cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); return true; } return false; } -static void cursor_surface_enter(void *data, - struct wl_surface * /*wl_surface*/, - struct wl_output *output) +static void cursor_surface_handle_enter(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { input_t *input = static_cast<input_t *>(data); for (const output_t *reg_output : input->system->outputs()) { - if (reg_output->output == output) { + if (reg_output->wl_output == output) { input->cursor.outputs.insert(reg_output); } } update_cursor_scale(input->cursor, input->system->shm()); } -static void cursor_surface_leave(void *data, - struct wl_surface * /*wl_surface*/, - struct wl_output *output) +static void cursor_surface_handle_leave(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { input_t *input = static_cast<input_t *>(data); for (const output_t *reg_output : input->system->outputs()) { - if (reg_output->output == output) { + if (reg_output->wl_output == output) { input->cursor.outputs.erase(reg_output); } } @@ -1049,8 +1057,8 @@ static void cursor_surface_leave(void *data, } struct wl_surface_listener cursor_surface_listener = { - cursor_surface_enter, - cursor_surface_leave, + cursor_surface_handle_enter, + cursor_surface_handle_leave, }; /** \} */ @@ -1059,15 +1067,14 @@ struct wl_surface_listener cursor_surface_listener = { /** \name Listener (Pointer), #wl_pointer_listener * \{ */ -static void pointer_enter(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t surface_x, - wl_fixed_t surface_y) +static void pointer_handle_enter(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y) { - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface)); - + GHOST_WindowWayland *win = window_from_surface(surface); if (!win) { return; } @@ -1092,13 +1099,12 @@ static void pointer_enter(void *data, GHOST_TABLET_DATA_NONE)); } -static void pointer_leave(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t /*serial*/, - struct wl_surface *surface) +static void pointer_handle_leave(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*serial*/, + struct wl_surface *surface) { - GHOST_IWindow *win = get_window(surface); - + GHOST_IWindow *win = window_from_surface(surface); if (!win) { return; } @@ -1107,16 +1113,14 @@ static void pointer_leave(void *data, static_cast<GHOST_WindowWayland *>(win)->deactivate(); } -static void pointer_motion(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t /*time*/, - wl_fixed_t surface_x, - wl_fixed_t surface_y) +static void pointer_handle_motion(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + wl_fixed_t surface_x, + wl_fixed_t surface_y) { input_t *input = static_cast<input_t *>(data); - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_pointer)); - + GHOST_WindowWayland *win = window_from_surface(input->focus_pointer); if (!win) { return; } @@ -1133,17 +1137,15 @@ static void pointer_motion(void *data, GHOST_TABLET_DATA_NONE)); } -static void pointer_button(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t serial, - uint32_t /*time*/, - uint32_t button, - uint32_t state) +static void pointer_handle_button(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t button, + uint32_t state) { input_t *input = static_cast<input_t *>(data); - - GHOST_IWindow *win = get_window(input->focus_pointer); - + GHOST_IWindow *win = window_from_surface(input->focus_pointer); if (!win) { return; } @@ -1189,16 +1191,14 @@ static void pointer_button(void *data, input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE)); } -static void pointer_axis(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t /*time*/, - uint32_t axis, - wl_fixed_t value) +static void pointer_handle_axis(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + uint32_t axis, + wl_fixed_t value) { input_t *input = static_cast<input_t *>(data); - - GHOST_IWindow *win = get_window(input->focus_pointer); - + GHOST_IWindow *win = window_from_surface(input->focus_pointer); if (!win) { return; } @@ -1212,11 +1212,11 @@ static void pointer_axis(void *data, } static const struct wl_pointer_listener pointer_listener = { - pointer_enter, - pointer_leave, - pointer_motion, - pointer_button, - pointer_axis, + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, }; /** \} */ @@ -1225,39 +1225,41 @@ static const struct wl_pointer_listener pointer_listener = { /** \name Listener (Tablet Tool), #zwp_tablet_tool_v2_listener * \{ */ -static void tablet_tool_type(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t tool_type) +static void tablet_tool_handle_type(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t tool_type) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); tool_input->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type); } -static void tablet_tool_hardware_serial(void * /*data*/, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*hardware_serial_hi*/, - uint32_t /*hardware_serial_lo*/) +static void tablet_tool_handle_hardware_serial(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*hardware_serial_hi*/, + uint32_t /*hardware_serial_lo*/) { } -static void tablet_tool_hardware_id_wacom(void * /*data*/, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*hardware_id_hi*/, - uint32_t /*hardware_id_lo*/) +static void tablet_tool_handle_hardware_id_wacom( + void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*hardware_id_hi*/, + uint32_t /*hardware_id_lo*/) { } -static void tablet_tool_capability(void * /*data*/, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*capability*/) +static void tablet_tool_handle_capability(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*capability*/) { } -static void tablet_tool_done(void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) +static void tablet_tool_handle_done(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) { } -static void tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; @@ -1269,11 +1271,11 @@ static void tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *zwp_table delete tool_input; } -static void tablet_tool_proximity_in(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t serial, - struct zwp_tablet_v2 * /*tablet*/, - struct wl_surface *surface) +static void tablet_tool_handle_proximity_in(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t serial, + struct zwp_tablet_v2 * /*tablet*/, + struct wl_surface *surface) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; @@ -1285,13 +1287,6 @@ static void tablet_tool_proximity_in(void *data, input->data_source_serial = serial; /* Update #GHOST_TabletData. */ - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); - if (!win) { - return; - } - - win->activate(); - GHOST_TabletData &td = tool_input->data; /* Reset, to avoid using stale tilt/pressure. */ td.Xtilt = 0.0f; @@ -1299,28 +1294,35 @@ static void tablet_tool_proximity_in(void *data, /* In case pressure isn't supported. */ td.Pressure = 1.0f; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + win->activate(); + win->setCursorShape(win->getCursorShape()); } -static void tablet_tool_proximity_out(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) +static void tablet_tool_handle_proximity_out(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); - input->focus_tablet = nullptr; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } win->setCursorShape(win->getCursorShape()); } -static void tablet_tool_down(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t serial) +static void tablet_tool_handle_down(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t serial) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); if (!win) { return; } @@ -1333,15 +1335,15 @@ static void tablet_tool_down(void *data, input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); } -static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) +static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); if (!win) { return; } + const GHOST_TEventType etype = GHOST_kEventButtonUp; const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; input->buttons.set(ebutton, false); @@ -1349,15 +1351,14 @@ static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_ input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); } -static void tablet_tool_motion(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t x, - wl_fixed_t y) +static void tablet_tool_handle_motion(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t x, + wl_fixed_t y) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); if (!win) { return; } @@ -1374,34 +1375,33 @@ static void tablet_tool_motion(void *data, tool_input->data)); } -static void tablet_tool_pressure(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t pressure) +static void tablet_tool_handle_pressure(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t pressure) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); if (!win) { return; } + GHOST_TabletData &td = tool_input->data; td.Pressure = (float)pressure / 65535; } -static void tablet_tool_distance(void * /*data*/, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*distance*/) +static void tablet_tool_handle_distance(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*distance*/) { } -static void tablet_tool_tilt(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t tilt_x, - wl_fixed_t tilt_y) +static void tablet_tool_handle_tilt(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t tilt_x, + wl_fixed_t tilt_y) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); if (!win) { return; } @@ -1414,23 +1414,23 @@ static void tablet_tool_tilt(void *data, td.Ytilt = td.Ytilt < -1.0f ? -1.0f : (td.Ytilt > 1.0f ? 1.0f : td.Ytilt); } -static void tablet_tool_rotation(void * /*data*/, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t /*degrees*/) +static void tablet_tool_handle_rotation(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t /*degrees*/) { /* Pass. */ } -static void tablet_tool_slider(void * /*data*/, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - int32_t /*position*/) +static void tablet_tool_handle_slider(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + int32_t /*position*/) { /* Pass. */ } -static void tablet_tool_wheel(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t /*degrees*/, - int32_t clicks) +static void tablet_tool_handle_wheel(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t /*degrees*/, + int32_t clicks) { if (clicks == 0) { return; @@ -1438,22 +1438,22 @@ static void tablet_tool_wheel(void *data, tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); if (!win) { return; } + input->system->pushEvent(new GHOST_EventWheel(input->system->getMilliSeconds(), win, clicks)); } -static void tablet_tool_button(void *data, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t serial, - uint32_t button, - uint32_t state) +static void tablet_tool_handle_button(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t serial, + uint32_t button, + uint32_t state) { tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); input_t *input = tool_input->input; - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_tablet)); + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); if (!win) { return; } @@ -1486,32 +1486,32 @@ static void tablet_tool_button(void *data, input->system->pushEvent(new GHOST_EventButton( input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); } -static void tablet_tool_frame(void * /*data*/, - struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*time*/) +static void tablet_tool_handle_frame(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*time*/) { } static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = { - tablet_tool_type, - tablet_tool_hardware_serial, - tablet_tool_hardware_id_wacom, - tablet_tool_capability, - tablet_tool_done, - tablet_tool_removed, - tablet_tool_proximity_in, - tablet_tool_proximity_out, - tablet_tool_down, - tablet_tool_up, - tablet_tool_motion, - tablet_tool_pressure, - tablet_tool_distance, - tablet_tool_tilt, - tablet_tool_rotation, - tablet_tool_slider, - tablet_tool_wheel, - tablet_tool_button, - tablet_tool_frame, + tablet_tool_handle_type, + tablet_tool_handle_hardware_serial, + tablet_tool_handle_hardware_id_wacom, + tablet_tool_handle_capability, + tablet_tool_handle_done, + tablet_tool_handle_removed, + tablet_tool_handle_proximity_in, + tablet_tool_handle_proximity_out, + tablet_tool_handle_down, + tablet_tool_handle_up, + tablet_tool_handle_motion, + tablet_tool_handle_pressure, + tablet_tool_handle_distance, + tablet_tool_handle_tilt, + tablet_tool_handle_rotation, + tablet_tool_handle_slider, + tablet_tool_handle_wheel, + tablet_tool_handle_button, + tablet_tool_handle_frame, }; /** \} */ @@ -1520,16 +1520,16 @@ static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = { /** \name Listener (Table Seat), #zwp_tablet_seat_v2_listener * \{ */ -static void tablet_seat_tablet_added(void * /*data*/, - struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, - struct zwp_tablet_v2 * /*id*/) +static void tablet_seat_handle_tablet_added(void * /*data*/, + struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, + struct zwp_tablet_v2 * /*id*/) { /* Pass. */ } -static void tablet_seat_tool_added(void *data, - struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, - struct zwp_tablet_tool_v2 *id) +static void tablet_seat_handle_tool_added(void *data, + struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, + struct zwp_tablet_tool_v2 *id) { input_t *input = static_cast<input_t *>(data); tablet_tool_input_t *tool_input = new tablet_tool_input_t(); @@ -1539,24 +1539,22 @@ static void tablet_seat_tool_added(void *data, tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor()); wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input); - tool_input->data = GHOST_TABLET_DATA_NONE; - zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input); input->tablet_tools.insert(id); } -static void tablet_seat_pad_added(void * /*data*/, - struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, - struct zwp_tablet_pad_v2 * /*id*/) +static void tablet_seat_handle_pad_added(void * /*data*/, + struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, + struct zwp_tablet_pad_v2 * /*id*/) { /* Pass. */ } const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { - tablet_seat_tablet_added, - tablet_seat_tool_added, - tablet_seat_pad_added, + tablet_seat_handle_tablet_added, + tablet_seat_handle_tool_added, + tablet_seat_handle_pad_added, }; /** \} */ @@ -1565,7 +1563,7 @@ const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { /** \name Listener (Keyboard), #wl_keyboard_listener * \{ */ -static void keyboard_keymap( +static void keyboard_handle_keymap( void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size) { input_t *input = static_cast<input_t *>(data); @@ -1601,11 +1599,11 @@ static void keyboard_keymap( * Notification that this seat's keyboard focus is on a certain * surface. */ -static void keyboard_enter(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - struct wl_surface *surface, - struct wl_array * /*keys*/) +static void keyboard_handle_enter(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface *surface, + struct wl_array * /*keys*/) { if (surface != nullptr) { static_cast<input_t *>(data)->focus_keyboard = surface; @@ -1618,10 +1616,10 @@ static void keyboard_enter(void *data, * Notification that this seat's keyboard focus is no longer on a * certain surface. */ -static void keyboard_leave(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - struct wl_surface *surface) +static void keyboard_handle_leave(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface *surface) { if (surface != nullptr) { static_cast<input_t *>(data)->focus_keyboard = nullptr; @@ -1654,12 +1652,12 @@ static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(struct xkb_state return sym; } -static void keyboard_key(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t serial, - uint32_t /*time*/, - uint32_t key, - uint32_t state) +static void keyboard_handle_key(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t key, + uint32_t state) { input_t *input = static_cast<input_t *>(data); @@ -1678,7 +1676,6 @@ static void keyboard_key(void *data, if (sym == XKB_KEY_NoSymbol) { return; } - const GHOST_TKey gkey = xkb_map_gkey(sym); /* Delete previous timer. */ if (xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) && @@ -1688,7 +1685,9 @@ static void keyboard_key(void *data, input->key_repeat.timer = nullptr; } - GHOST_TEventKeyData key_data; + GHOST_TEventKeyData key_data = { + .key = xkb_map_gkey(sym), + }; if (etype == GHOST_kEventKeyDown) { xkb_state_key_get_utf8( @@ -1703,7 +1702,7 @@ static void keyboard_key(void *data, GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( wl_surface_get_user_data(input->focus_keyboard)); input->system->pushEvent(new GHOST_EventKey( - input->system->getMilliSeconds(), etype, win, gkey, '\0', key_data.utf8_buf, false)); + input->system->getMilliSeconds(), etype, win, key_data.key, '\0', key_data.utf8_buf, false)); /* Start timer for repeating key, if applicable. */ if (input->key_repeat.rate > 0 && @@ -1713,33 +1712,32 @@ static void keyboard_key(void *data, key_repeat_payload_t *payload = new key_repeat_payload_t({ .system = input->system, .window = win, - .key = gkey, .key_data = key_data, }); - auto cb = [](GHOST_ITimerTask *task, uint64_t /*time*/) { + auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t /*time*/) { struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>( task->getUserData()); payload->system->pushEvent(new GHOST_EventKey(payload->system->getMilliSeconds(), GHOST_kEventKeyDown, payload->window, - payload->key, + payload->key_data.key, '\0', payload->key_data.utf8_buf, true)); }; input->key_repeat.timer = input->system->installTimer( - input->key_repeat.delay, 1000 / input->key_repeat.rate, cb, payload); + input->key_repeat.delay, 1000 / input->key_repeat.rate, key_repeat_fn, payload); } } -static void keyboard_modifiers(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, - uint32_t group) +static void keyboard_handle_modifiers(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) { xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state, mods_depressed, @@ -1750,10 +1748,10 @@ static void keyboard_modifiers(void *data, group); } -static void keyboard_repeat_info(void *data, - struct wl_keyboard * /*wl_keyboard*/, - int32_t rate, - int32_t delay) +static void keyboard_repeat_handle_info(void *data, + struct wl_keyboard * /*wl_keyboard*/, + int32_t rate, + int32_t delay) { input_t *input = static_cast<input_t *>(data); @@ -1762,12 +1760,12 @@ static void keyboard_repeat_info(void *data, } static const struct wl_keyboard_listener keyboard_listener = { - keyboard_keymap, - keyboard_enter, - keyboard_leave, - keyboard_key, - keyboard_modifiers, - keyboard_repeat_info, + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_repeat_handle_info, }; /** \} */ @@ -1776,40 +1774,113 @@ static const struct wl_keyboard_listener keyboard_listener = { /** \name Listener (Seat), #wl_seat_listener * \{ */ -static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { input_t *input = static_cast<input_t *>(data); - input->pointer = nullptr; - input->keyboard = nullptr; + input->wl_pointer = nullptr; + input->wl_keyboard = nullptr; if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - input->pointer = wl_seat_get_pointer(wl_seat); - input->cursor.surface = wl_compositor_create_surface(input->system->compositor()); + input->wl_pointer = wl_seat_get_pointer(wl_seat); + input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor()); input->cursor.visible = true; - input->cursor.buffer = nullptr; + input->cursor.wl_buffer = nullptr; input->cursor.file_buffer = new buffer_t; if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { input->cursor.theme_name = std::string(); input->cursor.size = default_cursor_size; } - wl_pointer_add_listener(input->pointer, &pointer_listener, data); - wl_surface_add_listener(input->cursor.surface, &cursor_surface_listener, data); + wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data); + wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - input->keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data); + input->wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(input->wl_keyboard, &keyboard_listener, data); } } -static void seat_name(void *data, struct wl_seat * /*wl_seat*/, const char *name) +static void seat_handle_name(void *data, struct wl_seat * /*wl_seat*/, const char *name) { static_cast<input_t *>(data)->name = std::string(name); } static const struct wl_seat_listener seat_listener = { - seat_capabilities, - seat_name, + seat_handle_capabilities, + seat_handle_name, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (XDG Output), #zxdg_output_v1_listener + * \{ */ + +static void xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 * /*xdg_output*/, + int32_t x, + int32_t y) +{ + output_t *output = static_cast<output_t *>(data); + output->position_logical[0] = x; + output->position_logical[1] = y; + output->has_position_logical = true; +} + +static void xdg_output_handle_logical_size(void *data, + struct zxdg_output_v1 * /*xdg_output*/, + int32_t width, + int32_t height) +{ + output_t *output = static_cast<output_t *>(data); + + if (output->size_logical[0] != 0 && output->size_logical[1] != 0) { + /* Original comment from SDL. */ + /* FIXME: GNOME has a bug where the logical size does not account for + * scale, resulting in bogus viewport sizes. + * + * Until this is fixed, validate that _some_ kind of scaling is being + * done (we can't match exactly because fractional scaling can't be + * detected otherwise), then override if necessary. + * -flibit + */ + if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) { + GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale"); + return; + } + } + + output->size_logical[0] = width; + output->size_logical[1] = height; + output->has_size_logical = true; +} + +static void xdg_output_handle_done(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/) +{ + /* NOTE: `xdg-output.done` events are deprecated and only apply below version 3 of the protocol. + * `wl-output.done` event will be emitted in version 3 or higher. */ +} + +static void xdg_output_handle_name(void * /*data*/, + struct zxdg_output_v1 * /*xdg_output*/, + const char * /*name*/) +{ + /* Pass. */ +} + +static void xdg_output_handle_description(void * /*data*/, + struct zxdg_output_v1 * /*xdg_output*/, + const char * /*description*/) +{ + /* Pass. */ +} + +static const struct zxdg_output_v1_listener xdg_output_listener = { + xdg_output_handle_logical_position, + xdg_output_handle_logical_size, + xdg_output_handle_done, + xdg_output_handle_name, + xdg_output_handle_description, }; /** \} */ @@ -1818,35 +1889,45 @@ static const struct wl_seat_listener seat_listener = { /** \name Listener (Output), #wl_output_listener * \{ */ -static void output_geometry(void *data, - struct wl_output * /*wl_output*/, - int32_t /*x*/, - int32_t /*y*/, - int32_t physical_width, - int32_t physical_height, - int32_t /*subpixel*/, - const char *make, - const char *model, - int32_t transform) +static void output_handle_geometry(void *data, + struct wl_output * /*wl_output*/, + int32_t /*x*/, + int32_t /*y*/, + int32_t physical_width, + int32_t physical_height, + int32_t /*subpixel*/, + const char *make, + const char *model, + int32_t transform) { output_t *output = static_cast<output_t *>(data); output->transform = transform; output->make = std::string(make); output->model = std::string(model); - output->width_mm = physical_width; - output->height_mm = physical_height; + output->size_mm[0] = physical_width; + output->size_mm[1] = physical_height; } -static void output_mode(void *data, - struct wl_output * /*wl_output*/, - uint32_t /*flags*/, - int32_t width, - int32_t height, - int32_t /*refresh*/) +static void output_handle_mode(void *data, + struct wl_output * /*wl_output*/, + uint32_t flags, + int32_t width, + int32_t height, + int32_t /*refresh*/) { output_t *output = static_cast<output_t *>(data); - output->width_pxl = width; - output->height_pxl = height; + + if (flags & WL_OUTPUT_MODE_CURRENT) { + output->size_native[0] = width; + output->size_native[1] = height; + + /* Don't rotate this yet, `wl-output` coordinates are transformed in + * handle_done and `xdg-output` coordinates are pre-transformed. */ + if (!output->has_size_logical) { + output->size_logical[0] = width; + output->size_logical[1] = height; + } + } } /** @@ -1857,20 +1938,43 @@ static void output_mode(void *data, * changes done after that. This allows changes to the output * properties to be seen as atomic, even if they happen via multiple events. */ -static void output_done(void * /*data*/, struct wl_output * /*wl_output*/) +static void output_handle_done(void *data, struct wl_output * /*wl_output*/) { + output_t *output = static_cast<output_t *>(data); + int32_t size_native[2]; + if (output->transform & WL_OUTPUT_TRANSFORM_90) { + size_native[0] = output->size_native[1]; + size_native[1] = output->size_native[1]; + } + else { + size_native[0] = output->size_native[0]; + size_native[1] = output->size_native[1]; + } + + /* If `xdg-output` is present, calculate the true scale of the desktop */ + if (output->has_size_logical) { + + /* NOTE: it's not necessary to divide these values by their greatest-common-denominator + * as even a 64k screen resolution doesn't approach overflowing an `int32_t`. */ + + GHOST_ASSERT(size_native[0] && output->size_logical[0], + "Screen size values were not set when they were expected to be."); + + output->scale_fractional = wl_fixed_from_int(size_native[0]) / output->size_logical[0]; + output->has_scale_fractional = true; + } } -static void output_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) +static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) { static_cast<output_t *>(data)->scale = factor; } static const struct wl_output_listener output_listener = { - output_geometry, - output_mode, - output_done, - output_scale, + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, }; /** \} */ @@ -1879,13 +1983,13 @@ static const struct wl_output_listener output_listener = { /** \name Listener (XDG WM Base), #xdg_wm_base_listener * \{ */ -static void shell_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) +static void shell_handle_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) { xdg_wm_base_pong(xdg_wm_base, serial); } static const struct xdg_wm_base_listener shell_listener = { - shell_ping, + shell_handle_ping, }; /** \} */ @@ -1894,11 +1998,11 @@ static const struct xdg_wm_base_listener shell_listener = { /** \name Listener (Registry), #wl_registry_listener * \{ */ -static void global_add(void *data, - struct wl_registry *wl_registry, - uint32_t name, - const char *interface, - uint32_t /*version*/) +static void global_handle_add(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t /*version*/) { struct display_t *display = static_cast<struct display_t *>(data); if (!strcmp(interface, wl_compositor_interface.name)) { @@ -1914,31 +2018,37 @@ static void global_add(void *data, display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>( wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1)); } + else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) { + display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>( + wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 3)); + for (output_t *output : display->outputs) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager, + output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + } + } else if (!strcmp(interface, wl_output_interface.name)) { output_t *output = new output_t; - output->scale = 1; - output->output = static_cast<wl_output *>( + output->wl_output = static_cast<wl_output *>( wl_registry_bind(wl_registry, name, &wl_output_interface, 2)); display->outputs.push_back(output); - wl_output_add_listener(output->output, &output_listener, output); + wl_output_add_listener(output->wl_output, &output_listener, output); + + if (display->xdg_output_manager) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager, + output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + } } else if (!strcmp(interface, wl_seat_interface.name)) { input_t *input = new input_t; input->system = display->system; input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - input->xkb_state = nullptr; - input->data_offer_dnd = nullptr; - input->data_offer_copy_paste = nullptr; input->data_source = new data_source_t; - input->data_source->data_source = nullptr; - input->data_source->buffer_out = nullptr; - input->relative_pointer = nullptr; - input->locked_pointer = nullptr; - input->confined_pointer = nullptr; - input->seat = static_cast<wl_seat *>( + input->wl_seat = static_cast<wl_seat *>( wl_registry_bind(wl_registry, name, &wl_seat_interface, 4)); display->inputs.push_back(input); - wl_seat_add_listener(input->seat, &seat_listener, input); + wl_seat_add_listener(input->wl_seat, &seat_listener, input); } else if (!strcmp(interface, wl_shm_interface.name)) { display->shm = static_cast<wl_shm *>( @@ -1971,13 +2081,15 @@ static void global_add(void *data, * name is no longer available. If the client bound to the global * using the bind request, the client should now destroy that object. */ -static void global_remove(void * /*data*/, struct wl_registry * /*wl_registry*/, uint32_t /*name*/) +static void global_handle_remove(void * /*data*/, + struct wl_registry * /*wl_registry*/, + uint32_t /*name*/) { } static const struct wl_registry_listener registry_listener = { - global_add, - global_remove, + global_handle_add, + global_handle_remove, }; /** \} */ @@ -2018,14 +2130,15 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) if (d->data_device_manager) { for (input_t *input : d->inputs) { input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager, - input->seat); + input->wl_seat); wl_data_device_add_listener(input->data_device, &data_device_listener, input); } } if (d->tablet_manager) { for (input_t *input : d->inputs) { - input->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager, input->seat); + input->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager, + input->wl_seat); zwp_tablet_seat_v2_add_listener(input->tablet_seat, &tablet_seat_listener, input); } } @@ -2163,6 +2276,10 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) co } GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)); + if (!win) { + return GHOST_kFailure; + } + const wl_fixed_t scale = win->scale(); x = wl_fixed_to_int(scale * input->xy[0]); y = wl_fixed_to_int(scale * input->xy[1]); @@ -2178,8 +2295,8 @@ void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &he { if (getNumDisplays() > 0) { /* We assume first output as main. */ - width = uint32_t(d->outputs[0]->width_pxl) / d->outputs[0]->scale; - height = uint32_t(d->outputs[0]->height_pxl) / d->outputs[0]->scale; + width = uint32_t(d->outputs[0]->size_native[0]) / d->outputs[0]->scale; + height = uint32_t(d->outputs[0]->size_native[1]) / d->outputs[0]->scale; } } @@ -2300,12 +2417,12 @@ wl_compositor *GHOST_SystemWayland::compositor() return d->compositor; } -xdg_wm_base *GHOST_SystemWayland::shell() +xdg_wm_base *GHOST_SystemWayland::xdg_shell() { return d->xdg_shell; } -zxdg_decoration_manager_v1 *GHOST_SystemWayland::decoration_manager() +zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager() { return d->xdg_decoration_manager; } @@ -2331,22 +2448,22 @@ static void set_cursor_buffer(input_t *input, wl_buffer *buffer) c->visible = (buffer != nullptr); - const int32_t image_size_x = int32_t(c->image.width); - const int32_t image_size_y = int32_t(c->image.height); + const int32_t image_size_x = int32_t(c->wl_image.width); + const int32_t image_size_y = int32_t(c->wl_image.height); - const int32_t hotspot_x = int32_t(c->image.hotspot_x) / c->scale; - const int32_t hotspot_y = int32_t(c->image.hotspot_y) / c->scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / c->scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / c->scale; - wl_surface_attach(c->surface, buffer, 0, 0); - wl_surface_damage(c->surface, 0, 0, image_size_x, image_size_y); + wl_surface_attach(c->wl_surface, buffer, 0, 0); + wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y); - wl_pointer_set_cursor(input->pointer, + wl_pointer_set_cursor(input->wl_pointer, input->pointer_serial, - c->visible ? c->surface : nullptr, + c->visible ? c->wl_surface : nullptr, hotspot_x, hotspot_y); - wl_surface_commit(c->surface); + wl_surface_commit(c->wl_surface); /* Set the cursor for all tablet tools as well. */ for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { @@ -2379,12 +2496,13 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) input_t *input = d->inputs[0]; cursor_t *c = &input->cursor; - if (!c->theme) { + if (!c->wl_theme) { /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */ - c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); + c->wl_theme = wl_cursor_theme_load( + c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); } - wl_cursor *cursor = wl_cursor_theme_get_cursor(c->theme, cursor_name.c_str()); + wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name.c_str()); if (!cursor) { GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl); @@ -2397,8 +2515,8 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) return GHOST_kFailure; } - c->buffer = buffer; - c->image = *image; + c->wl_buffer = buffer; + c->wl_image = *image; set_cursor_buffer(input, buffer); @@ -2504,11 +2622,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, } } - cursor->buffer = buffer; - cursor->image.width = uint32_t(sizex); - cursor->image.height = uint32_t(sizey); - cursor->image.hotspot_x = uint32_t(hotX); - cursor->image.hotspot_y = uint32_t(hotY); + cursor->wl_buffer = buffer; + cursor->wl_image.width = uint32_t(sizex); + cursor->wl_image.height = uint32_t(sizey); + cursor->wl_image.hotspot_x = uint32_t(hotX); + cursor->wl_image.hotspot_y = uint32_t(hotY); set_cursor_buffer(d->inputs[0], buffer); @@ -2526,7 +2644,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) cursor_t *cursor = &input->cursor; if (visible) { if (!cursor->visible) { - set_cursor_buffer(input, cursor->buffer); + set_cursor_buffer(input, cursor->wl_buffer); } } else { @@ -2545,7 +2663,6 @@ bool GHOST_SystemWayland::supportsCursorWarp() GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, const GHOST_TGrabCursorMode mode_current, - wl_surface *surface) { /* ignore, if the required protocols are not supported */ @@ -2597,39 +2714,45 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo if (input->locked_pointer) { /* Request location to restore to. */ if (mode_current == GHOST_kGrabWrap) { - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface)); - GHOST_Rect bounds; - int32_t xy_new[2] = {input->xy[0], input->xy[1]}; - - /* Fallback to window bounds. */ - if (win->getCursorGrabBounds(bounds) == GHOST_kFailure) { - win->getClientBounds(bounds); + /* The chance this fails is _very_ low. */ + GHOST_WindowWayland *win = window_from_surface(surface); + if (!win) { + GHOST_PRINT("could not find window from surface when un-grabbing!" << std::endl); } - - const int scale = win->scale(); - - bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale; - bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale; - bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale; - bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale; - - bounds.wrapPoint(xy_new[0], xy_new[1], 0, win->getCursorGrabAxis()); - - /* Push an event so the new location is registered. */ - if ((xy_new[0] != input->xy[0]) || (xy_new[1] != input->xy[1])) { - input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - win, - wl_fixed_to_int(scale * xy_new[0]), - wl_fixed_to_int(scale * xy_new[1]), - GHOST_TABLET_DATA_NONE)); + else { + GHOST_Rect bounds; + int32_t xy_new[2] = {input->xy[0], input->xy[1]}; + + /* Fallback to window bounds. */ + if (win->getCursorGrabBounds(bounds) == GHOST_kFailure) { + win->getClientBounds(bounds); + } + + const int scale = win->scale(); + + bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale; + bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale; + bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale; + bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale; + + bounds.wrapPoint(xy_new[0], xy_new[1], 0, win->getCursorGrabAxis()); + + /* Push an event so the new location is registered. */ + if ((xy_new[0] != input->xy[0]) || (xy_new[1] != input->xy[1])) { + input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(scale * xy_new[0]), + wl_fixed_to_int(scale * xy_new[1]), + GHOST_TABLET_DATA_NONE)); + } + input->xy[0] = xy_new[0]; + input->xy[1] = xy_new[1]; + + zwp_locked_pointer_v1_set_cursor_position_hint( + input->locked_pointer, xy_new[0], xy_new[1]); + wl_surface_commit(surface); } - input->xy[0] = xy_new[0]; - input->xy[1] = xy_new[1]; - - zwp_locked_pointer_v1_set_cursor_position_hint( - input->locked_pointer, xy_new[0], xy_new[1]); - wl_surface_commit(surface); } zwp_locked_pointer_v1_destroy(input->locked_pointer); @@ -2652,13 +2775,13 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo * An alternative could be to draw the cursor in software (and hide the real cursor), * or just accept a locked cursor on WAYLAND. */ input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( - d->relative_pointer_manager, input->pointer); + d->relative_pointer_manager, input->wl_pointer); zwp_relative_pointer_v1_add_listener( input->relative_pointer, &relative_pointer_listener, input); input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( d->pointer_constraints, surface, - input->pointer, + input->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); } @@ -2668,7 +2791,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo input->confined_pointer = zwp_pointer_constraints_v1_confine_pointer( d->pointer_constraints, surface, - input->pointer, + input->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); } diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 5b3e4f8ed75..3be108b1f88 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -22,11 +22,32 @@ class GHOST_WindowWayland; struct display_t; struct output_t { - struct wl_output *output; - int32_t width_pxl, height_pxl; /* Dimensions in pixel. */ - int32_t width_mm, height_mm; /* Dimensions in millimeter. */ - int transform; - int scale; + struct wl_output *wl_output = nullptr; + struct zxdg_output_v1 *xdg_output = nullptr; + /** Dimensions in pixels. */ + int32_t size_native[2] = {0, 0}; + /** Dimensions in millimeter. */ + int32_t size_mm[2] = {0, 0}; + + int32_t size_logical[2] = {0, 0}; + bool has_size_logical = false; + + int32_t position_logical[2] = {0, 0}; + bool has_position_logical = false; + + int transform = 0; + int scale = 1; + /** + * The integer `scale` value should be used in almost all cases, + * as this is what is used for most API calls. + * Only use fractional scaling to calculate the DPI. + * + * \note Internally an #wl_fixed_t is used to store the scale of the display, + * so use the same value here (avoid floating point arithmetic in general). + */ + wl_fixed_t scale_fractional = wl_fixed_from_int(1); + bool has_scale_fractional = false; + std::string make; std::string model; }; @@ -79,9 +100,9 @@ class GHOST_SystemWayland : public GHOST_System { wl_compositor *compositor(); - xdg_wm_base *shell(); + xdg_wm_base *xdg_shell(); - zxdg_decoration_manager_v1 *decoration_manager(); + zxdg_decoration_manager_v1 *xdg_decoration_manager(); const std::vector<output_t *> &outputs() const; diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 68d50c597d6..fdd372a9bd4 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -20,8 +20,8 @@ static constexpr size_t base_dpi = 96; struct window_t { - GHOST_WindowWayland *w; - wl_surface *surface; + GHOST_WindowWayland *w = nullptr; + struct wl_surface *wl_surface = nullptr; /** * Outputs on which the window is currently shown on. * @@ -31,22 +31,27 @@ struct window_t { std::vector<const output_t *> outputs; /** The scale value written to #wl_surface_set_buffer_scale. */ - int scale; - /** The DPI (currently always `scale * base_dpi`). */ - uint16_t dpi; + int scale = 0; + /** + * The DPI, either: + * - `scale * base_dpi` + * - `wl_fixed_to_int(scale_fractional * base_dpi)` + * When fractional scaling is available. + */ + uint32_t dpi = 0; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; + struct xdg_surface *xdg_surface = nullptr; + struct xdg_toplevel *xdg_toplevel = nullptr; struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr; - enum zxdg_toplevel_decoration_v1_mode decoration_mode; - wl_egl_window *egl_window; - bool is_maximised; - bool is_fullscreen; - bool is_active; - bool is_dialog; - - int32_t size[2]; - int32_t size_pending[2]; + enum zxdg_toplevel_decoration_v1_mode decoration_mode = (enum zxdg_toplevel_decoration_v1_mode)0; + wl_egl_window *egl_window = nullptr; + bool is_maximised = false; + bool is_fullscreen = false; + bool is_active = false; + bool is_dialog = false; + + int32_t size[2] = {0, 0}; + int32_t size_pending[2] = {0, 0}; }; /* -------------------------------------------------------------------- */ @@ -54,25 +59,42 @@ struct window_t { * \{ */ static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs, - const int scale_default) + const int32_t scale_default, + uint32_t *r_dpi) { int scale_max = 0; + const output_t *output_max = nullptr; for (const output_t *reg_output : outputs) { - scale_max = std::max(scale_max, reg_output->scale); + if (scale_max < reg_output->scale) { + scale_max = reg_output->scale; + output_max = reg_output; + } + } + + if (scale_max != 0) { + if (r_dpi) { + *r_dpi = output_max->has_scale_fractional ? + /* Fractional DPI. */ + wl_fixed_to_int(output_max->scale_fractional * base_dpi) : + /* Simple non-fractional DPI. */ + (scale_max * base_dpi); + } + return scale_max; + } + + if (r_dpi) { + *r_dpi = scale_default * base_dpi; } - return scale_max ? scale_max : scale_default; + return scale_default; } /** \} */ /* -------------------------------------------------------------------- */ -/** \name Wayland Interface Callbacks - * - * These callbacks are registered for Wayland interfaces and called when - * an event is received from the compositor. +/** \name Listener (XDG Top Level), #xdg_toplevel_listener * \{ */ -static void toplevel_configure( +static void xdg_toplevel_handle_configure( void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states) { window_t *win = static_cast<window_t *>(data); @@ -105,17 +127,23 @@ static void toplevel_configure( } } -static void toplevel_close(void *data, xdg_toplevel * /*xdg_toplevel*/) +static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/) { static_cast<window_t *>(data)->w->close(); } static const xdg_toplevel_listener toplevel_listener = { - toplevel_configure, - toplevel_close, + xdg_toplevel_handle_configure, + xdg_toplevel_handle_close, }; -static void toplevel_decoration_configure( +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (XDG Decoration Listener), #zxdg_toplevel_decoration_v1_listener + * \{ */ + +static void xdg_toplevel_decoration_handle_configure( void *data, struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/, uint32_t mode) @@ -124,10 +152,16 @@ static void toplevel_decoration_configure( } static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = { - toplevel_decoration_configure, + xdg_toplevel_decoration_handle_configure, }; -static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (XDG Surface Handle Configure), #xdg_surface_listener + * \{ */ + +static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) { window_t *win = static_cast<window_t *>(data); @@ -154,11 +188,19 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser xdg_surface_ack_configure(xdg_surface, serial); } -static const xdg_surface_listener surface_listener = { - surface_configure, +static const xdg_surface_listener xdg_surface_listener = { + xdg_surface_handle_configure, }; -static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (Surface), #wl_surface_listener + * \{ */ + +static void surface_handle_enter(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); output_t *reg_output = w->output_find_by_wl(output); @@ -175,7 +217,9 @@ static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct w->outputs_changed_update_scale(); } -static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) +static void surface_handle_leave(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); output_t *reg_output = w->output_find_by_wl(output); @@ -193,8 +237,8 @@ static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct } struct wl_surface_listener wl_surface_listener = { - surface_enter, - surface_leave, + surface_handle_enter, + surface_handle_leave, }; /** \} */ @@ -242,18 +286,17 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, * * Using the maximum scale is best as it results in the window first being smaller, * avoiding a large window flashing before it's made smaller. */ - w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1); - w->dpi = w->scale * base_dpi; + w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1, &w->dpi); /* Window surfaces. */ - w->surface = wl_compositor_create_surface(m_system->compositor()); + w->wl_surface = wl_compositor_create_surface(m_system->compositor()); wl_surface_set_buffer_scale(this->surface(), w->scale); - wl_surface_add_listener(w->surface, &wl_surface_listener, this); + wl_surface_add_listener(w->wl_surface, &wl_surface_listener, this); - w->egl_window = wl_egl_window_create(w->surface, int(w->size[0]), int(w->size[1])); + w->egl_window = wl_egl_window_create(w->wl_surface, int(w->size[0]), int(w->size[1])); - w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface); + w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface); w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); /* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels. @@ -262,18 +305,18 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, * when the `w->scale` changed. */ xdg_toplevel_set_min_size(w->xdg_toplevel, 320, 240); - if (m_system->decoration_manager()) { + if (m_system->xdg_decoration_manager()) { w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - m_system->decoration_manager(), w->xdg_toplevel); + m_system->xdg_decoration_manager(), w->xdg_toplevel); zxdg_toplevel_decoration_v1_add_listener( w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w); zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } - wl_surface_set_user_data(w->surface, this); + wl_surface_set_user_data(w->wl_surface, this); - xdg_surface_add_listener(w->xdg_surface, &surface_listener, w); + xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w); xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); if (parentWindow && is_dialog) { @@ -282,7 +325,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, } /* Call top-level callbacks. */ - wl_surface_commit(w->surface); + wl_surface_commit(w->wl_surface); wl_display_roundtrip(m_system->display()); #ifdef GHOST_OPENGL_ALPHA @@ -336,7 +379,7 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size() wl_surface *GHOST_WindowWayland::surface() const { - return w->surface; + return w->wl_surface; } std::vector<const output_t *> &GHOST_WindowWayland::outputs() @@ -347,7 +390,7 @@ std::vector<const output_t *> &GHOST_WindowWayland::outputs() output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output) { for (output_t *reg_output : this->m_system->outputs()) { - if (reg_output->output == output) { + if (reg_output->wl_output == output) { return reg_output; } } @@ -356,28 +399,35 @@ output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output) bool GHOST_WindowWayland::outputs_changed_update_scale() { - const int scale_next = outputs_max_scale_or_default(this->m_system->outputs(), 0); + uint32_t dpi_next; + const int scale_next = outputs_max_scale_or_default(this->m_system->outputs(), 0, &dpi_next); if (scale_next == 0) { return false; } + window_t *win = this->w; + const uint32_t dpi_curr = win->dpi; const int scale_curr = win->scale; - if (scale_next == scale_curr) { - return false; - } + bool changed = false; - /* Unlikely but possible there is a pending size change is set. */ - win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; - win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; + if (scale_next != scale_curr) { + /* Unlikely but possible there is a pending size change is set. */ + win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; + win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; - win->scale = scale_next; - wl_surface_set_buffer_scale(this->surface(), scale_next); + win->scale = scale_next; + wl_surface_set_buffer_scale(this->surface(), scale_next); + changed = true; + } - /* Using the real DPI will cause wrong scaling of the UI - * use a multiplier for the default DPI as workaround. */ - win->dpi = scale_next * base_dpi; + if (dpi_next != dpi_curr) { + /* Using the real DPI will cause wrong scaling of the UI + * use a multiplier for the default DPI as workaround. */ + win->dpi = dpi_next; + changed = true; + } - return true; + return changed; } uint16_t GHOST_WindowWayland::dpi() @@ -392,7 +442,7 @@ int GHOST_WindowWayland::scale() GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { - return m_system->setCursorGrab(mode, m_cursorGrab, w->surface); + return m_system->setCursorGrab(mode, m_cursorGrab, w->wl_surface); } GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape) @@ -484,7 +534,7 @@ GHOST_WindowWayland::~GHOST_WindowWayland() } xdg_toplevel_destroy(w->xdg_toplevel); xdg_surface_destroy(w->xdg_surface); - wl_surface_destroy(w->surface); + wl_surface_destroy(w->wl_surface); delete w; } diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h index 8fec725e122..d7cd6608556 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.h +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -17,7 +17,6 @@ class GHOST_SystemWayland; struct output_t; struct window_t; -struct wl_surface; class GHOST_WindowWayland : public GHOST_Window { public: @@ -102,7 +101,7 @@ class GHOST_WindowWayland : public GHOST_Window { GHOST_TSuccess notify_size(); - wl_surface *surface() const; + struct wl_surface *surface() const; std::vector<const output_t *> &outputs(); diff --git a/release/datafiles/icons/ops.curves.density.dat b/release/datafiles/icons/ops.curves.sculpt_density.dat Binary files differindex f336de79082..f336de79082 100644 --- a/release/datafiles/icons/ops.curves.density.dat +++ b/release/datafiles/icons/ops.curves.sculpt_density.dat diff --git a/release/datafiles/icons/ops.curves.slide.dat b/release/datafiles/icons/ops.curves.sculpt_slide.dat Binary files differindex 7f143ad92cc..7f143ad92cc 100644 --- a/release/datafiles/icons/ops.curves.slide.dat +++ b/release/datafiles/icons/ops.curves.sculpt_slide.dat diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 21fa1e88627..f61d8465952 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5588,6 +5588,17 @@ def km_font(params): return keymap +# Curves edit mode. +def km_curves(params): + items = [] + keymap = ( + "Curves", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + return keymap + def km_sculpt_curves(params): items = [] @@ -8013,6 +8024,7 @@ def generate_keymaps(params=None): km_lattice(params), km_particle(params), km_font(params), + km_curves(params), km_sculpt_curves(params), km_object_non_modal(params), diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index d8d387036ff..a806e9e0c33 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -199,7 +199,8 @@ class CLIP_HT_header(Header): row = layout.row(align=True) row.prop(dopesheet, "sort_method", text="") row.prop(dopesheet, "use_invert_sort", - text="Invert", toggle=True) + text="", toggle=True, + icon='SORT_DESC' if dopesheet.use_invert_sort else 'SORT_ASC') def _draw_masking(self, context): layout = self.layout diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 5755ddb3018..13eefd27bec 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -64,7 +64,7 @@ bool BKE_id_attribute_remove(struct ID *id, const char *name, struct ReportList * Creates a duplicate attribute layer. */ struct CustomDataLayer *BKE_id_attribute_duplicate(struct ID *id, - struct CustomDataLayer *layer, + const char *name, struct ReportList *reports); struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, @@ -72,7 +72,7 @@ struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, int type, eAttrDomain domain); -struct CustomDataLayer *BKE_id_attribute_search(const struct ID *id, +struct CustomDataLayer *BKE_id_attribute_search(struct ID *id, const char *name, eCustomDataMask type, eAttrDomainMask domain_mask); diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h index 4ad17610207..271026d4253 100644 --- a/source/blender/blenkernel/BKE_subdiv_modifier.h +++ b/source/blender/blenkernel/BKE_subdiv_modifier.h @@ -7,6 +7,8 @@ #pragma once +#include "BKE_subdiv.h" + #include "BLI_sys_types.h" #ifdef __cplusplus @@ -24,9 +26,30 @@ struct Subdiv; struct SubdivSettings; struct SubsurfModifierData; -void BKE_subsurf_modifier_subdiv_settings_init(struct SubdivSettings *settings, - const struct SubsurfModifierData *smd, - bool use_render_params); +/* Runtime subsurf modifier data, cached in modifier on evaluated meshes. */ +typedef struct SubsurfRuntimeData { + /* Subdivision settings, exists before descriptor or mesh wrapper is created. */ + SubdivSettings settings; + + /* Cached subdivision surface descriptor, with topology and settings. */ + struct Subdiv *subdiv; + bool set_by_draw_code; + + /* Cached mesh wrapper data, to be used for GPU subdiv or lazy evaluation on CPU. */ + bool has_gpu_subdiv; + int resolution; + bool use_optimal_display; + bool calc_loop_normals; + bool use_loop_normals; + + /* Cached from the draw code for stats display. */ + int stats_totvert; + int stats_totedge; + int stats_totpoly; + int stats_totloop; +} SubsurfRuntimeData; + +bool BKE_subsurf_modifier_runtime_init(struct SubsurfModifierData *smd, bool use_render_params); bool BKE_subsurf_modifier_use_custom_loop_normals(const struct SubsurfModifierData *smd, const struct Mesh *mesh); @@ -57,12 +80,7 @@ extern void (*BKE_subsurf_modifier_free_gpu_cache_cb)(struct Subdiv *subdiv); * which matches settings and topology. */ struct Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure( - const struct SubsurfModifierData *smd, - const struct SubdivSettings *subdiv_settings, - const struct Mesh *mesh, - bool for_draw_code); - -struct SubsurfRuntimeData *BKE_subsurf_modifier_ensure_runtime(struct SubsurfModifierData *smd); + struct SubsurfRuntimeData *runtime_data, const struct Mesh *mesh, bool for_draw_code); /** * Return the #ModifierMode required for the evaluation of the subsurf modifier, diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 8496692ceb3..b3a9d894944 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -52,6 +52,7 @@ #include "BKE_object.h" #include "BKE_object_deform.h" #include "BKE_paint.h" +#include "BKE_subdiv_modifier.h" #include "BLI_sys_types.h" /* for intptr_t support */ @@ -598,10 +599,10 @@ static bool mesh_has_modifier_final_normals(const Mesh *mesh_input, /* Test if mesh has the required loop normals, in case an additional modifier * evaluation from another instance or from an operator requests it but the * initial normals were not loop normals. */ - const bool do_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); + const bool calc_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || + (final_datamask->lmask & CD_MASK_NORMAL) != 0); - return (!do_loop_normals || CustomData_has_layer(&mesh_final->ldata, CD_NORMAL)); + return (!calc_loop_normals || CustomData_has_layer(&mesh_final->ldata, CD_NORMAL)); } static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, @@ -610,16 +611,19 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, Mesh *mesh_final) { /* Compute normals. */ - const bool do_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); + const bool calc_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || + (final_datamask->lmask & CD_MASK_NORMAL) != 0); /* Needed as `final_datamask` is not preserved outside modifier stack evaluation. */ - mesh_final->runtime.subsurf_do_loop_normals = do_loop_normals; + SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime.subsurf_runtime_data; + if (subsurf_runtime_data) { + subsurf_runtime_data->calc_loop_normals = calc_loop_normals; + } - if (do_loop_normals) { + if (calc_loop_normals) { /* Compute loop normals (NOTE: will compute poly and vert normals as well, if needed!). In case * of deferred CPU subdivision, this will be computed when the wrapper is generated. */ - if (mesh_final->runtime.subsurf_resolution == 0) { + if (!subsurf_runtime_data || subsurf_runtime_data->resolution == 0) { BKE_mesh_calc_normals_split(mesh_final); } } @@ -1266,15 +1270,18 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, return; } - const bool do_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); + const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || + (final_datamask->lmask & CD_MASK_NORMAL) != 0); - mesh_final->runtime.subsurf_do_loop_normals = do_loop_normals; + SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime.subsurf_runtime_data; + if (subsurf_runtime_data) { + subsurf_runtime_data->calc_loop_normals = calc_loop_normals; + } - if (do_loop_normals) { + if (calc_loop_normals) { /* Compute loop normals. In case of deferred CPU subdivision, this will be computed when the * wrapper is generated. */ - if (mesh_final->runtime.subsurf_resolution == 0) { + if (!subsurf_runtime_data || subsurf_runtime_data->resolution == 0) { BKE_mesh_calc_normals_split(mesh_final); } } diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index b2ea8b833b3..7c09b4a4ce3 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -208,53 +208,36 @@ CustomDataLayer *BKE_id_attribute_new( return (index == -1) ? nullptr : &(customdata->layers[index]); } -CustomDataLayer *BKE_id_attribute_duplicate(ID *id, CustomDataLayer *layer, ReportList *reports) +CustomDataLayer *BKE_id_attribute_duplicate(ID *id, const char *name, ReportList *reports) { - DomainInfo info[ATTR_DOMAIN_NUM]; - get_domains(id, info); - - eCustomDataType type = (eCustomDataType)layer->type; - eAttrDomain domain = BKE_id_attribute_domain(id, layer); - - CustomData *customdata = info[domain].customdata; - if (customdata == nullptr) { - BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type"); + const CustomDataLayer *src_layer = BKE_id_attribute_search( + id, name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); + if (src_layer == nullptr) { + BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry"); return nullptr; } - char name[MAX_CUSTOMDATA_LAYER_NAME]; - char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; + const eCustomDataType type = (eCustomDataType)src_layer->type; + const eAttrDomain domain = BKE_id_attribute_domain(id, src_layer); /* Make a copy of name in case CustomData API reallocates the layers. */ - BLI_strncpy(name, layer->name, MAX_CUSTOMDATA_LAYER_NAME); - BKE_id_attribute_calc_unique_name(id, layer->name, uniquename); + const std::string name_copy = name; - switch (GS(id->name)) { - case ID_ME: { - Mesh *me = (Mesh *)id; - BMEditMesh *em = me->edit_mesh; - if (em != nullptr) { - BM_data_layer_add_named(em->bm, customdata, type, uniquename); - } - else { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - } - break; - } - default: { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - break; - } + DomainInfo info[ATTR_DOMAIN_NUM]; + get_domains(id, info); + CustomData *customdata = info[domain].customdata; + + CustomDataLayer *new_layer = BKE_id_attribute_new(id, name_copy.c_str(), type, domain, reports); + if (new_layer == nullptr) { + return nullptr; } - int from_index = CustomData_get_named_layer_index(customdata, type, name); - int to_index = CustomData_get_named_layer_index(customdata, type, uniquename); + const int from_index = CustomData_get_named_layer_index(customdata, type, name_copy.c_str()); + const int to_index = CustomData_get_named_layer_index(customdata, type, new_layer->name); CustomData_copy_data_layer( customdata, customdata, from_index, to_index, 0, 0, info[domain].length); - return (to_index == -1) ? nullptr : &(customdata->layers[to_index]); + return new_layer; } bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports) @@ -317,7 +300,7 @@ CustomDataLayer *BKE_id_attribute_find(const ID *id, return nullptr; } -CustomDataLayer *BKE_id_attribute_search(const ID *id, +CustomDataLayer *BKE_id_attribute_search(ID *id, const char *name, const eCustomDataMask type_mask, const eAttrDomainMask domain_mask) diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index c505a74724b..fdebf1d6a26 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -314,28 +314,23 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) return me; } + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; + if (runtime_data == nullptr || runtime_data->settings.level == 0) { + return me; + } + /* Initialize the settings before ensuring the descriptor as this is checked to decide whether * subdivision is needed at all, and checking the descriptor status might involve checking if the * data is out-of-date, which is a very expensive operation. */ SubdivToMeshSettings mesh_settings; - mesh_settings.resolution = me->runtime.subsurf_resolution; - mesh_settings.use_optimal_display = me->runtime.subsurf_use_optimal_display; + mesh_settings.resolution = runtime_data->resolution; + mesh_settings.use_optimal_display = runtime_data->use_optimal_display; if (mesh_settings.resolution < 3) { return me; } - const bool apply_render = me->runtime.subsurf_apply_render; - - SubdivSettings subdiv_settings; - BKE_subsurf_modifier_subdiv_settings_init(&subdiv_settings, smd, apply_render); - if (subdiv_settings.level == 0) { - return me; - } - - SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); - - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &subdiv_settings, me, false); + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, me, false); if (subdiv == nullptr) { /* Happens on bad topology, but also on empty input mesh. */ return me; @@ -358,7 +353,7 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) CustomData_set_layer_flag(&me->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); CustomData_set_layer_flag(&subdiv_mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); } - else if (me->runtime.subsurf_do_loop_normals) { + else if (runtime_data->calc_loop_normals) { BKE_mesh_calc_normals_split(subdiv_mesh); } diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index a5f6c453ed4..8f54d71108a 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -244,24 +244,42 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const } } -static void update_active_strip_from_listbase(AnimData *adt_dest, - NlaTrack *track_dest, - const NlaStrip *active_strip, - const ListBase /* NlaStrip */ *strips_source) +/** + * Find `active_strip` in `strips_source`, then return the strip with the same + * index from `strips_dest`. + */ +static NlaStrip *find_active_strip_from_listbase(const NlaStrip *active_strip, + const ListBase /* NlaStrip */ *strips_source, + const ListBase /* NlaStrip */ *strips_dest) { - NlaStrip *strip_dest = track_dest->strips.first; + NlaStrip *strip_dest = strips_dest->first; LISTBASE_FOREACH (const NlaStrip *, strip_source, strips_source) { - if (strip_source == active_strip) { - adt_dest->actstrip = strip_dest; - return; + if (strip_dest == NULL) { + /* The tracks are assumed to have an equal number of strips, but this is not the case when + * dragging multiple strips. The transform system merges the selected strips into one + * meta-strip, reducing the number of strips in `track_dest`. */ + break; } - - if (strip_source->type == NLASTRIP_TYPE_META) { - update_active_strip_from_listbase(adt_dest, track_dest, active_strip, &strip_source->strips); + if (strip_source == active_strip) { + return strip_dest; + } + + const bool src_is_meta = strip_source->type == NLASTRIP_TYPE_META; + const bool dst_is_meta = strip_dest->type == NLASTRIP_TYPE_META; + BLI_assert_msg(src_is_meta == dst_is_meta, + "Expecting topology of source and destination strips to be equal"); + if (src_is_meta && dst_is_meta) { + NlaStrip *found_in_meta = find_active_strip_from_listbase( + active_strip, &strip_source->strips, &strip_dest->strips); + if (found_in_meta != NULL) { + return found_in_meta; + } } strip_dest = strip_dest->next; } + + return NULL; } /* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */ @@ -272,8 +290,9 @@ static void update_active_strip(AnimData *adt_dest, { BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips)); - update_active_strip_from_listbase( - adt_dest, track_dest, adt_source->actstrip, &track_source->strips); + NlaStrip *active_strip = find_active_strip_from_listbase( + adt_source->actstrip, &track_source->strips, &track_dest->strips); + adt_dest->actstrip = active_strip; } /* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */ diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 3a69a33105e..3b20bdc826c 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1391,7 +1391,7 @@ void pbvh_free_draw_buffers(PBVH *pbvh, PBVHNode *node) } } -static void pbvh_check_draw_layout(PBVH *pbvh, bool full_render) +static void pbvh_check_draw_layout(PBVH *pbvh) { const CustomData *vdata; const CustomData *ldata; @@ -1419,8 +1419,14 @@ static void pbvh_check_draw_layout(PBVH *pbvh, bool full_render) break; } - /* rebuild all draw buffers if attribute layout changed */ - if (GPU_pbvh_attribute_names_update(pbvh->type, pbvh->vbo_id, vdata, ldata, !full_render)) { + /* Rebuild all draw buffers if attribute layout changed. + * + * NOTE: The optimization where we only send active attributes + * to the GPU in workbench mode is disabled due to bugs + * (there's no guarantee there isn't another EEVEE viewport which would + * free the draw buffers and corrupt the draw cache). + */ + if (GPU_pbvh_attribute_names_update(pbvh->type, pbvh->vbo_id, vdata, ldata, false)) { /* attribute layout changed; force rebuild */ for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; @@ -2861,7 +2867,7 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, PBVHFrustumPlanes *draw_frustum, void (*draw_fn)(void *user_data, GPU_PBVH_Buffers *buffers), void *user_data, - bool full_render) + bool UNUSED(full_render)) { PBVHNode **nodes; int totnode; @@ -2884,7 +2890,7 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, update_flag = PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers; } - pbvh_check_draw_layout(pbvh, full_render); + pbvh_check_draw_layout(pbvh); /* Update draw buffers. */ if (totnode != 0 && (update_flag & (PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers))) { diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index e43da956ce5..f5423dccc0f 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -3,8 +3,6 @@ #include "BKE_subdiv_modifier.h" -#include "BLI_session_uuid.h" - #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" @@ -21,22 +19,40 @@ #include "opensubdiv_capi.h" -void BKE_subsurf_modifier_subdiv_settings_init(SubdivSettings *settings, - const SubsurfModifierData *smd, - const bool use_render_params) +bool BKE_subsurf_modifier_runtime_init(SubsurfModifierData *smd, const bool use_render_params) { const int requested_levels = (use_render_params) ? smd->renderLevels : smd->levels; - settings->is_simple = (smd->subdivType == SUBSURF_TYPE_SIMPLE); - settings->is_adaptive = !(smd->flags & eSubsurfModifierFlag_UseRecursiveSubdivision); - settings->level = settings->is_simple ? - 1 : - (settings->is_adaptive ? smd->quality : requested_levels); - settings->use_creases = (smd->flags & eSubsurfModifierFlag_UseCrease); - settings->vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( + SubdivSettings settings; + settings.is_simple = (smd->subdivType == SUBSURF_TYPE_SIMPLE); + settings.is_adaptive = !(smd->flags & eSubsurfModifierFlag_UseRecursiveSubdivision); + settings.level = settings.is_simple ? 1 : + (settings.is_adaptive ? smd->quality : requested_levels); + settings.use_creases = (smd->flags & eSubsurfModifierFlag_UseCrease); + settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( smd->boundary_smooth); - settings->fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( + settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( smd->uv_smooth); + + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; + if (settings.level == 0) { + /* Modifier is effectively disabled, but still update settings if runtime data + * was already allocated. */ + if (runtime_data) { + runtime_data->settings = settings; + } + + return false; + } + else { + /* Allocate runtime data if it did not exist yet. */ + if (runtime_data == NULL) { + runtime_data = MEM_callocN(sizeof(*runtime_data), "subsurf runtime"); + smd->modifier.runtime = runtime_data; + } + runtime_data->settings = settings; + return true; + } } static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene, @@ -133,37 +149,27 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, bool BKE_subsurf_modifier_has_gpu_subdiv(const Mesh *mesh) { - return BLI_session_uuid_is_generated(&mesh->runtime.subsurf_session_uuid); + SubsurfRuntimeData *runtime_data = mesh->runtime.subsurf_runtime_data; + return runtime_data && runtime_data->has_gpu_subdiv; } void (*BKE_subsurf_modifier_free_gpu_cache_cb)(Subdiv *subdiv) = NULL; -Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(const SubsurfModifierData *smd, - const SubdivSettings *subdiv_settings, +Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(SubsurfRuntimeData *runtime_data, const Mesh *mesh, const bool for_draw_code) { - SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; if (runtime_data->subdiv && runtime_data->set_by_draw_code != for_draw_code) { BKE_subdiv_free(runtime_data->subdiv); runtime_data->subdiv = NULL; } - Subdiv *subdiv = BKE_subdiv_update_from_mesh(runtime_data->subdiv, subdiv_settings, mesh); + Subdiv *subdiv = BKE_subdiv_update_from_mesh( + runtime_data->subdiv, &runtime_data->settings, mesh); runtime_data->subdiv = subdiv; runtime_data->set_by_draw_code = for_draw_code; return subdiv; } -SubsurfRuntimeData *BKE_subsurf_modifier_ensure_runtime(SubsurfModifierData *smd) -{ - SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; - if (runtime_data == NULL) { - runtime_data = MEM_callocN(sizeof(*runtime_data), "subsurf runtime"); - smd->modifier.runtime = runtime_data; - } - return runtime_data; -} - int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode) { if (is_final_render) { diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index f9d3a44e5cb..3506d0e347a 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -336,6 +336,10 @@ void BKE_tracking_settings_init(MovieTracking *tracking) tracking->stabilization.filter = TRACKING_FILTER_BILINEAR; tracking->stabilization.flag |= TRACKING_SHOW_STAB_TRACKS; + /* Descending order of average error: tracks with the highest error are on top. */ + tracking->dopesheet.sort_method = TRACKING_DOPE_SORT_AVERAGE_ERROR; + tracking->dopesheet.flag |= TRACKING_DOPE_SORT_INVERSE; + BKE_tracking_object_add(tracking, "Camera"); } @@ -2904,6 +2908,10 @@ static int channels_average_error_sort(const void *a, const void *b) return 1; } + if (channel_a->track->error == channel_b->track->error) { + return channels_alpha_sort(a, b); + } + return 0; } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl index 280b31ea463..c0d4393f2da 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl @@ -5,6 +5,6 @@ void main() vec3 world_pos = point_object_to_world(vec3(au, 0.0)); gl_Position = point_world_to_ndc(world_pos); - finalColor = ((flag & FACE_UV_SELECT) != 0) ? colorVertexSelect : vec4(colorWire.rgb, 1.0); + finalColor = ((flag & FACE_UV_SELECT) != 0) ? colorFaceDot : vec4(colorWire.rgb, 1.0); gl_PointSize = pointSize; } diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index 2c07b651c7c..992ffe16a14 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -163,7 +163,7 @@ void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode) cache->is_dirty = true; break; default: - BLI_assert(0); + BLI_assert_unreachable(); } } @@ -530,8 +530,6 @@ static bool curves_ensure_attributes(const Curves &curves, } switch (type) { - default: - break; case CD_PROP_BOOL: case CD_PROP_INT8: case CD_PROP_INT32: @@ -548,6 +546,8 @@ static bool curves_ensure_attributes(const Curves &curves, } break; } + default: + break; } } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index 96b5910072b..7c02ee2c033 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -2068,8 +2068,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } if (do_subdivision) { - DRW_create_subdivision(scene, - ob, + DRW_create_subdivision(ob, me, cache, &cache->final, diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 2b0953b0b9b..b37a420b555 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1052,14 +1052,10 @@ static void build_vertex_face_adjacency_maps(DRWSubdivCache *cache) static bool draw_subdiv_build_cache(DRWSubdivCache *cache, Subdiv *subdiv, Mesh *mesh_eval, - const Scene *scene, - const SubsurfModifierData *smd, - const bool is_final_render) + const SubsurfRuntimeData *runtime_data) { - const int requested_levels = (is_final_render) ? smd->renderLevels : smd->levels; - const int level = get_render_subsurf_level(&scene->r, requested_levels, is_final_render); SubdivToMeshSettings to_mesh_settings; - to_mesh_settings.resolution = (1 << level) + 1; + to_mesh_settings.resolution = runtime_data->resolution; to_mesh_settings.use_optimal_display = false; if (cache->resolution != to_mesh_settings.resolution) { @@ -2006,8 +2002,7 @@ static void draw_subdiv_cache_ensure_mat_offsets(DRWSubdivCache *cache, MEM_freeN(per_polygon_mat_offset); } -static bool draw_subdiv_create_requested_buffers(const Scene *scene, - Object *ob, +static bool draw_subdiv_create_requested_buffers(Object *ob, Mesh *mesh, struct MeshBatchCache *batch_cache, MeshBufferCache *mbc, @@ -2021,16 +2016,10 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const bool use_hide, OpenSubdiv_EvaluatorCache *evaluator_cache) { - SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>( - BKE_modifiers_findby_session_uuid(ob, &mesh->runtime.subsurf_session_uuid)); - BLI_assert(smd); + SubsurfRuntimeData *runtime_data = mesh->runtime.subsurf_runtime_data; + BLI_assert(runtime_data && runtime_data->has_gpu_subdiv); - const bool is_final_render = DRW_state_is_scene_render(); - - SubdivSettings settings; - BKE_subsurf_modifier_subdiv_settings_init(&settings, smd, is_final_render); - - if (settings.level == 0) { + if (runtime_data->settings.level == 0) { return false; } @@ -2041,9 +2030,7 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, bm = mesh->edit_mesh->bm; } - BKE_subsurf_modifier_ensure_runtime(smd); - - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &settings, mesh_eval, true); + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, mesh_eval, true); if (!subdiv) { return false; } @@ -2064,13 +2051,13 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, } DRWSubdivCache *draw_cache = mesh_batch_cache_ensure_subdiv_cache(batch_cache); - if (!draw_subdiv_build_cache(draw_cache, subdiv, mesh_eval, scene, smd, is_final_render)) { + if (!draw_subdiv_build_cache(draw_cache, subdiv, mesh_eval, runtime_data)) { return false; } /* Edges which do not come from coarse edges should not be drawn in edit mode, only in object * mode when optimal display in turned off. */ - const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges) || is_editmode; + const bool optimal_display = runtime_data->use_optimal_display || is_editmode; draw_cache->bm = bm; draw_cache->mesh = mesh_eval; @@ -2078,14 +2065,13 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, draw_cache->optimal_display = optimal_display; draw_cache->num_subdiv_triangles = tris_count_from_number_of_loops(draw_cache->num_subdiv_loops); - /* Copy topology information for stats display. Use `mesh` directly, as `mesh_eval` could be the - * edit mesh. */ - mesh->runtime.subsurf_totvert = draw_cache->num_subdiv_verts; - mesh->runtime.subsurf_totedge = draw_cache->num_subdiv_edges; - mesh->runtime.subsurf_totpoly = draw_cache->num_subdiv_quads; - mesh->runtime.subsurf_totloop = draw_cache->num_subdiv_loops; + /* Copy topology information for stats display. */ + runtime_data->stats_totvert = draw_cache->num_subdiv_verts; + runtime_data->stats_totedge = draw_cache->num_subdiv_edges; + runtime_data->stats_totpoly = draw_cache->num_subdiv_quads; + runtime_data->stats_totloop = draw_cache->num_subdiv_loops; - draw_cache->use_custom_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && + draw_cache->use_custom_loop_normals = (runtime_data->use_loop_normals) && (mesh_eval->flag & ME_AUTOSMOOTH) && CustomData_has_layer(&mesh_eval->ldata, CD_CUSTOMLOOPNORMAL); @@ -2207,8 +2193,7 @@ blender::Span<DRWSubdivLooseVertex> draw_subdiv_cache_get_loose_verts(const DRWS static OpenSubdiv_EvaluatorCache *g_evaluator_cache = nullptr; -void DRW_create_subdivision(const Scene *scene, - Object *ob, +void DRW_create_subdivision(Object *ob, Mesh *mesh, struct MeshBatchCache *batch_cache, MeshBufferCache *mbc, @@ -2231,8 +2216,7 @@ void DRW_create_subdivision(const Scene *scene, const double begin_time = PIL_check_seconds_timer(); #endif - if (!draw_subdiv_create_requested_buffers(scene, - ob, + if (!draw_subdiv_create_requested_buffers(ob, mesh, batch_cache, mbc, diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index 8920a2dcd51..2d9f4713feb 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -185,8 +185,7 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache); /** \} */ -void DRW_create_subdivision(const struct Scene *scene, - struct Object *ob, +void DRW_create_subdivision(struct Object *ob, struct Mesh *mesh, struct MeshBatchCache *batch_cache, struct MeshBufferCache *mbc, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index 5f07f8dfb72..fb6b5e1904b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -27,21 +27,16 @@ namespace blender::draw { static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, eAttrDomain domain) { switch (domain) { - default: { - return nullptr; - } - case ATTR_DOMAIN_POINT: { + case ATTR_DOMAIN_POINT: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - } - case ATTR_DOMAIN_CORNER: { + case ATTR_DOMAIN_CORNER: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - } - case ATTR_DOMAIN_FACE: { + case ATTR_DOMAIN_FACE: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - } - case ATTR_DOMAIN_EDGE: { + case ATTR_DOMAIN_EDGE: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata; - } + default: + return nullptr; } } @@ -50,7 +45,7 @@ static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, eAttrDom * etc.) directly map to available GPU types. Booleans are still converted as attributes are vec4 * in the shader. */ -template<typename AttributeType, typename VBOType> struct attribute_type_converter { +template<typename AttributeType, typename VBOType> struct AttributeTypeConverter { static VBOType convert_value(AttributeType value) { if constexpr (std::is_same_v<AttributeType, VBOType>) { @@ -67,7 +62,7 @@ struct gpuMeshCol { ushort r, g, b, a; }; -template<> struct attribute_type_converter<MPropCol, gpuMeshCol> { +template<> struct AttributeTypeConverter<MPropCol, gpuMeshCol> { static gpuMeshCol convert_value(MPropCol value) { gpuMeshCol result; @@ -86,53 +81,42 @@ static uint gpu_component_size_for_attribute_type(eCustomDataType type) case CD_PROP_BOOL: case CD_PROP_INT8: case CD_PROP_INT32: - case CD_PROP_FLOAT: { + case CD_PROP_FLOAT: /* TODO(@kevindietrich): should be 1 when scalar attributes conversion is handled by us. See * comment #extract_attr_init. */ return 3; - } - case CD_PROP_FLOAT2: { + case CD_PROP_FLOAT2: return 2; - } - case CD_PROP_FLOAT3: { + case CD_PROP_FLOAT3: return 3; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: return 4; - } - default: { + default: return 0; - } } } static GPUVertFetchMode get_fetch_mode_for_type(eCustomDataType type) { switch (type) { - case CD_PROP_INT32: { + case CD_PROP_INT32: return GPU_FETCH_INT_TO_FLOAT; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: return GPU_FETCH_INT_TO_FLOAT_UNIT; - } - default: { + default: return GPU_FETCH_FLOAT; - } } } static GPUVertCompType get_comp_type_for_type(eCustomDataType type) { switch (type) { - case CD_PROP_INT32: { + case CD_PROP_INT32: return GPU_COMP_I32; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: return GPU_COMP_U16; - } - default: { + default: return GPU_COMP_F32; - } } } @@ -184,41 +168,36 @@ static void fill_vertbuf_with_attribute(const MeshRenderData *mr, const AttributeType *attr_data = static_cast<const AttributeType *>( CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); - using converter = attribute_type_converter<AttributeType, VBOType>; + using Converter = AttributeTypeConverter<AttributeType, VBOType>; switch (request.domain) { - default: { - BLI_assert(false); - break; - } - case ATTR_DOMAIN_POINT: { + case ATTR_DOMAIN_POINT: for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { - *vbo_data = converter::convert_value(attr_data[mloop->v]); + *vbo_data = Converter::convert_value(attr_data[mloop->v]); } break; - } - case ATTR_DOMAIN_CORNER: { + case ATTR_DOMAIN_CORNER: for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) { - *vbo_data = converter::convert_value(attr_data[ml_index]); + *vbo_data = Converter::convert_value(attr_data[ml_index]); } break; - } - case ATTR_DOMAIN_EDGE: { + case ATTR_DOMAIN_EDGE: for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { - *vbo_data = converter::convert_value(attr_data[mloop->e]); + *vbo_data = Converter::convert_value(attr_data[mloop->e]); } break; - } - case ATTR_DOMAIN_FACE: { + case ATTR_DOMAIN_FACE: for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { const MPoly &poly = mpoly[mp_index]; - const VBOType value = converter::convert_value(attr_data[mp_index]); + const VBOType value = Converter::convert_value(attr_data[mp_index]); for (int l = 0; l < poly.totloop; l++) { *vbo_data++ = value; } } break; - } + default: + BLI_assert_unreachable(); + break; } } @@ -231,9 +210,9 @@ static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, BLI_assert(custom_data); const int layer_index = request.layer_index; - int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); + const int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); - using converter = attribute_type_converter<AttributeType, VBOType>; + using Converter = AttributeTypeConverter<AttributeType, VBOType>; BMIter f_iter; BMFace *efa; @@ -255,10 +234,10 @@ static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs)); } else { - BLI_assert(false); + BLI_assert_unreachable(); continue; } - *vbo_data = converter::convert_value(*attr_data); + *vbo_data = Converter::convert_value(*attr_data); vbo_data++; } while ((l_iter = l_iter->next) != l_first); } @@ -297,37 +276,29 @@ static void extract_attr_init(const MeshRenderData *mr, * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar * texture as for volume attribute, so we can control the conversion ourselves. */ switch (request.cd_type) { - case CD_PROP_BOOL: { + case CD_PROP_BOOL: extract_attr_generic<bool, float3>(mr, vbo, request); break; - } - case CD_PROP_INT8: { + case CD_PROP_INT8: extract_attr_generic<int8_t, float3>(mr, vbo, request); break; - } - case CD_PROP_INT32: { + case CD_PROP_INT32: extract_attr_generic<int32_t, float3>(mr, vbo, request); break; - } - case CD_PROP_FLOAT: { + case CD_PROP_FLOAT: extract_attr_generic<float, float3>(mr, vbo, request); break; - } - case CD_PROP_FLOAT2: { + case CD_PROP_FLOAT2: extract_attr_generic<float2>(mr, vbo, request); break; - } - case CD_PROP_FLOAT3: { + case CD_PROP_FLOAT3: extract_attr_generic<float3>(mr, vbo, request); break; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: extract_attr_generic<MPropCol, gpuMeshCol>(mr, vbo, request); break; - } - default: { - BLI_assert(false); - } + default: + BLI_assert_unreachable(); } } @@ -353,37 +324,29 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_data_alloc(src_data, static_cast<uint32_t>(coarse_mesh->totloop)); switch (request.cd_type) { - case CD_PROP_BOOL: { + case CD_PROP_BOOL: extract_attr_generic<bool, float3>(mr, src_data, request); break; - } - case CD_PROP_INT8: { + case CD_PROP_INT8: extract_attr_generic<int8_t, float3>(mr, src_data, request); break; - } - case CD_PROP_INT32: { + case CD_PROP_INT32: extract_attr_generic<int32_t, float3>(mr, src_data, request); break; - } - case CD_PROP_FLOAT: { + case CD_PROP_FLOAT: extract_attr_generic<float, float3>(mr, src_data, request); break; - } - case CD_PROP_FLOAT2: { + case CD_PROP_FLOAT2: extract_attr_generic<float2>(mr, src_data, request); break; - } - case CD_PROP_FLOAT3: { + case CD_PROP_FLOAT3: extract_attr_generic<float3>(mr, src_data, request); break; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: extract_attr_generic<MPropCol, gpuMeshCol>(mr, src_data, request); break; - } - default: { - BLI_assert(false); - } + default: + BLI_assert_unreachable(); } GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); @@ -430,7 +393,7 @@ EXTRACT_INIT_WRAPPER(12) EXTRACT_INIT_WRAPPER(13) EXTRACT_INIT_WRAPPER(14) -template<int index> +template<int Index> constexpr MeshExtract create_extractor_attr(ExtractInitFn fn, ExtractInitSubdivFn subdiv_fn) { MeshExtract extractor = {nullptr}; @@ -439,7 +402,7 @@ constexpr MeshExtract create_extractor_attr(ExtractInitFn fn, ExtractInitSubdivF extractor.data_type = MR_DATA_NONE; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[Index]); return extractor; } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 34ae7697d2d..a7abaab88ff 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -772,17 +772,17 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curve.pen ops.curve.radius ops.curve.vertex_random - ops.curves.density ops.curves.sculpt_add ops.curves.sculpt_comb ops.curves.sculpt_cut ops.curves.sculpt_delete + ops.curves.sculpt_density ops.curves.sculpt_grow_shrink ops.curves.sculpt_pinch ops.curves.sculpt_puff + ops.curves.sculpt_slide ops.curves.sculpt_smooth ops.curves.sculpt_snake_hook - ops.curves.slide ops.generic.cursor ops.generic.select ops.generic.select_box diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 37ec7a61bcb..c7e782b7b89 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -518,15 +518,18 @@ static int geometry_color_attribute_duplicate_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); ID *id = static_cast<ID *>(ob->data); - CustomDataLayer *layer = BKE_id_attributes_active_color_get(id); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get(id); if (layer == nullptr) { return OPERATOR_CANCELLED; } - CustomDataLayer *newLayer = BKE_id_attribute_duplicate(id, layer, op->reports); + CustomDataLayer *new_layer = BKE_id_attribute_duplicate(id, layer->name, op->reports); + if (new_layer == nullptr) { + return OPERATOR_CANCELLED; + } - BKE_id_attributes_active_color_set(id, newLayer); + BKE_id_attributes_active_color_set(id, new_layer); DEG_id_tag_update(id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, id); @@ -539,6 +542,10 @@ static bool geometry_color_attributes_duplicate_poll(bContext *C) if (!geometry_attributes_poll(C)) { return false; } + if (CTX_data_edit_object(C) != nullptr) { + CTX_wm_operator_poll_msg_set(C, "Operation is not allowed in edit mode"); + return false; + } Object *ob = ED_object_context(C); ID *data = ob ? static_cast<ID *>(ob->data) : nullptr; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 5ffcdbd9668..6d2f45813fe 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2945,6 +2945,9 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value) { + /* Caller should check. */ + BLI_assert((but->flag & UI_BUT_DISABLED) == 0); + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); ui_textedit_string_set(but, but->active, value); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 60a68e270bf..aafb56119ae 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1942,6 +1942,24 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \name Drop Name Operator * \{ */ +static bool drop_name_poll(bContext *C) +{ + if (!ED_operator_regionactive(C)) { + return false; + } + + const uiBut *but = UI_but_active_drop_name_button(C); + if (!but) { + return false; + } + + if (but->flag & UI_BUT_DISABLED) { + return false; + } + + return true; +} + static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { uiBut *but = UI_but_active_drop_name_button(C); @@ -1961,7 +1979,7 @@ static void UI_OT_drop_name(wmOperatorType *ot) ot->idname = "UI_OT_drop_name"; ot->description = "Drop name to button"; - ot->poll = ED_operator_regionactive; + ot->poll = drop_name_poll; ot->invoke = drop_name_invoke; ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index a8eed136df3..4819ae09785 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -100,6 +100,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.export_selected_objects = RNA_boolean_get(op->ptr, "export_selected_objects"); export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv"); export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals"); + export_params.export_colors = RNA_boolean_get(op->ptr, "export_colors"); export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials"); export_params.path_mode = RNA_enum_get(op->ptr, "path_mode"); export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh"); @@ -160,6 +161,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export")); uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE); uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE); + uiItemR(sub, imfptr, "export_colors", 0, IFACE_("Colors"), ICON_NONE); uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE); uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE); uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE); @@ -315,6 +317,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export Normals", "Export per-face normals if the face is flat-shaded, per-face-per-loop " "normals if smooth-shaded"); + RNA_def_boolean(ot->srna, "export_colors", false, "Export Colors", "Export per-vertex colors"); RNA_def_boolean(ot->srna, "export_materials", true, diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 7c0b6fb0a93..e5fe108b6cd 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -364,12 +364,9 @@ static int select_exec(bContext *C, wmOperator *op) return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } if (deselect_all) { - /* For clip editor tracks, leave deselect all to clip editor. */ - if (!ED_clip_can_select(C)) { - ED_mask_deselect_all(C); - ED_mask_view_lock_state_restore_no_jump(C, &lock_state); - return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; - } + ED_mask_deselect_all(C); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } return OPERATOR_PASS_THROUGH; diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index bbfbbd2cc58..28e304bfa74 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -394,8 +394,6 @@ static int select_exec(bContext *C, wmOperator *op) else if (deselect_all) { ed_tracking_deselect_all_tracks(tracksbase); ed_tracking_deselect_all_plane_tracks(plane_tracks_base); - /* Mask as well if we are in combined mask / track view. */ - ED_mask_deselect_all(C); } ED_clip_view_lock_state_restore_no_jump(C, &lock_state); diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index b817ff887ce..29a7eb150a1 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -46,6 +46,7 @@ #include "BKE_pbvh.h" #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" +#include "BKE_subdiv_modifier.h" #include "DEG_depsgraph_query.h" @@ -92,15 +93,18 @@ static bool stats_mesheval(const Mesh *me_eval, bool is_selected, SceneStats *st } int totvert, totedge, totface, totloop; - if (me_eval->runtime.subdiv_ccg != nullptr) { - const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg; + + const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg; + const SubsurfRuntimeData *subsurf_runtime_data = me_eval->runtime.subsurf_runtime_data; + + if (subdiv_ccg != nullptr) { BKE_subdiv_ccg_topology_counters(subdiv_ccg, &totvert, &totedge, &totface, &totloop); } - else if (me_eval->runtime.subsurf_resolution != 0) { - totvert = me_eval->runtime.subsurf_totvert; - totedge = me_eval->runtime.subsurf_totedge; - totface = me_eval->runtime.subsurf_totpoly; - totloop = me_eval->runtime.subsurf_totloop; + else if (subsurf_runtime_data && subsurf_runtime_data->resolution != 0) { + totvert = subsurf_runtime_data->stats_totvert; + totedge = subsurf_runtime_data->stats_totedge; + totface = subsurf_runtime_data->stats_totpoly; + totloop = subsurf_runtime_data->stats_totloop; } else { totvert = me_eval->totvert; diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 475d02020d0..ec19e8d5e5b 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -222,13 +222,33 @@ static void unlink_action_fn(bContext *C, } static void unlink_material_fn(bContext *UNUSED(C), - ReportList *UNUSED(reports), + ReportList *reports, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tsep, - TreeStoreElem *UNUSED(tselem), + TreeStoreElem *tselem, void *UNUSED(user_data)) { + const bool te_is_material = TSE_IS_REAL_ID(tselem) && (GS(tselem->id->name) == ID_MA); + + if (!te_is_material) { + /* Just fail silently. Another element may be selected that is a material, we don't want to + * confuse users with an error in that case. */ + return; + } + + if (!tsep || !TSE_IS_REAL_ID(tsep)) { + /* Valid case, no parent element of the material or it is not an ID (could be a #TSE_ID_BASE + * for example) so there's no data to unlink from. */ + BKE_reportf(reports, + RPT_WARNING, + "Cannot unlink material '%s'. It's not clear which object or object-data it " + "should be unlinked from, there's no object or object-data as parent in the " + "Outliner tree", + tselem->id->name + 2); + return; + } + Material **matar = nullptr; int a, totcol = 0; diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index ea6d3351eaa..647d13a4d56 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -340,7 +340,7 @@ static void seq_load_apply_generic_options(bContext *C, wmOperator *op, Sequence ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); SEQ_collection_free(strip_col); } @@ -863,7 +863,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C, ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); } SEQ_collection_free(strip_col); @@ -913,7 +913,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); } SEQ_collection_free(strip_col); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 75966d4f070..86c438c616e 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -581,21 +581,13 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve static void sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) { - /* Iterate in reverse so meta-strips are iterated after their children. */ for (int i = data->num_seq - 1; i >= 0; i--) { Sequence *seq = data->seq_array[i]; - int endframe; - /* Offset seq start. */ seq->start = data->ts[i].start + offset; - if (data->trim[i]) { - /* Find the end-frame. */ - endframe = seq->start + seq->len; - - /* Compute the sequence offsets. */ - seq->endofs = endframe - SEQ_time_right_handle_frame_get(seq); - seq->startofs = SEQ_time_left_handle_frame_get(seq) - seq->start; + seq->startofs = data->ts[i].startofs - offset; + seq->endofs = data->ts[i].endofs + offset; } } @@ -2397,6 +2389,13 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq) return; } + /* Add curves for strips inside meta strip. */ + if (seq->type == SEQ_TYPE_META) { + LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) { + sequencer_copy_animation(scene, meta_child); + } + } + GSet *fcurves = SEQ_fcurves_by_strip_get(seq, &scene->adt->action->curves); if (fcurves == NULL) { return; @@ -2406,6 +2405,7 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq) BLI_addtail(&fcurves_clipboard, BKE_fcurve_copy(fcu)); } GSET_FOREACH_END(); + BLI_gset_free(fcurves, NULL); } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 68f04aab969..226b0f84f14 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -281,296 +281,21 @@ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips } } -static bool seq_transform_check_overlap(SeqCollection *transformed_strips) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if (seq->flag & SEQ_OVERLAP) { - return true; - } - } - return false; -} - -static SeqCollection *extract_standalone_strips(SeqCollection *transformed_strips) -{ - SeqCollection *collection = SEQ_collection_create(__func__); - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == NULL) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - -/* Query strips positioned after left edge of transformed strips bound-box. */ -static SeqCollection *query_right_side_strips(ListBase *seqbase, SeqCollection *transformed_strips) -{ - int minframe = MAXFRAME; - { - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - minframe = min_ii(minframe, SEQ_time_left_handle_frame_get(seq)); - } - } - - SeqCollection *collection = SEQ_collection_create(__func__); - LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if ((seq->flag & SELECT) == 0 && SEQ_time_left_handle_frame_get(seq) >= minframe) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - static ListBase *seqbase_active_get(const TransInfo *t) { Editing *ed = SEQ_editing_get(t->scene); return SEQ_active_seqbase_get(ed); } -/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal - * to overlap of transformed strips. */ -static void seq_transform_handle_expand_to_fit(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) -{ - ListBase *markers = &scene->markers; - - SeqCollection *right_side_strips = query_right_side_strips(seqbasep, transformed_strips); - - /* Temporarily move right side strips beyond timeline boundary. */ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, right_side_strips) { - seq->machine += MAXSEQ * 2; - } - - /* Shuffle transformed standalone strips. This is because transformed strips can overlap with - * strips on left side. */ - SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); - SEQ_transform_seqbase_shuffle_time( - standalone_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(standalone_strips); - - /* Move temporarily moved strips back to their original place and tag for shuffling. */ - SEQ_ITERATOR_FOREACH (seq, right_side_strips) { - seq->machine -= MAXSEQ * 2; - } - /* Shuffle again to displace strips on right side. Final effect shuffling is done in - * SEQ_transform_handle_overlap. */ - SEQ_transform_seqbase_shuffle_time( - right_side_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(right_side_strips); -} - -static SeqCollection *query_overwrite_targets(ListBase *seqbasep, - SeqCollection *transformed_strips) -{ - SeqCollection *collection = SEQ_query_unselected_strips(seqbasep); - - Sequence *seq, *seq_transformed; - SEQ_ITERATOR_FOREACH (seq, collection) { - bool does_overlap = false; - - SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) { - /* Effects of transformed strips can be unselected. These must not be included. */ - if (seq == seq_transformed) { - SEQ_collection_remove_strip(seq, collection); - } - if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) { - does_overlap = true; - } - } - - if (!does_overlap) { - SEQ_collection_remove_strip(seq, collection); - } - } - - return collection; -} - -typedef enum eOvelapDescrition { - /* No overlap. */ - STRIP_OVERLAP_NONE, - /* Overlapping strip covers overlapped completely. */ - STRIP_OVERLAP_IS_FULL, - /* Overlapping strip is inside overlapped. */ - STRIP_OVERLAP_IS_INSIDE, - /* Partial overlap between 2 strips. */ - STRIP_OVERLAP_LEFT_SIDE, - STRIP_OVERLAP_RIGHT_SIDE, -} eOvelapDescrition; - -static eOvelapDescrition overlap_description_get(const Sequence *transformed, - const Sequence *target) -{ - if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && - SEQ_time_right_handle_frame_get(transformed) >= SEQ_time_right_handle_frame_get(target)) { - return STRIP_OVERLAP_IS_FULL; - } - if (SEQ_time_left_handle_frame_get(transformed) > SEQ_time_left_handle_frame_get(target) && - SEQ_time_right_handle_frame_get(transformed) < SEQ_time_right_handle_frame_get(target)) { - return STRIP_OVERLAP_IS_INSIDE; - } - if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && - SEQ_time_left_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { - return STRIP_OVERLAP_LEFT_SIDE; - } - if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_right_handle_frame_get(target) && - SEQ_time_right_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { - return STRIP_OVERLAP_RIGHT_SIDE; - } - return STRIP_OVERLAP_NONE; -} - -/* Split strip in 3 parts, remove middle part and fit transformed inside. */ -static void seq_transform_handle_overwrite_split(Scene *scene, - ListBase *seqbasep, - const Sequence *transformed, - Sequence *target) -{ - /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can pass - * NULL here. */ - Main *bmain = NULL; - - Sequence *split_strip = SEQ_edit_strip_split(bmain, - scene, - seqbasep, - target, - SEQ_time_left_handle_frame_get(transformed), - SEQ_SPLIT_SOFT, - NULL); - SEQ_edit_strip_split(bmain, - scene, - seqbasep, - split_strip, - SEQ_time_right_handle_frame_get(transformed), - SEQ_SPLIT_SOFT, - NULL); - SEQ_edit_flag_for_removal(scene, seqbasep, split_strip); - SEQ_edit_remove_flagged_sequences(scene, seqbasep); -} - -/* Trim strips by adjusting handle position. - * This is bit more complicated in case overlap happens on effect. */ -static void seq_transform_handle_overwrite_trim(Scene *scene, - ListBase *seqbasep, - const Sequence *transformed, - Sequence *target, - const eOvelapDescrition overlap) -{ - SeqCollection *targets = SEQ_query_by_reference(target, seqbasep, SEQ_query_strip_effect_chain); - - /* Expand collection by adding all target's children, effects and their children. */ - if ((target->type & SEQ_TYPE_EFFECT) != 0) { - SEQ_collection_expand(seqbasep, targets, SEQ_query_strip_effect_chain); - } - - /* Trim all non effects, that have influence on effect length which is overlapping. */ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, targets) { - if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) { - continue; - } - if (overlap == STRIP_OVERLAP_LEFT_SIDE) { - SEQ_time_left_handle_frame_set(scene, seq, SEQ_time_right_handle_frame_get(transformed)); - } - else { - BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); - SEQ_time_right_handle_frame_set(scene, seq, SEQ_time_left_handle_frame_get(transformed)); - } - } - SEQ_collection_free(targets); -} - -static void seq_transform_handle_overwrite(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips) -{ - SeqCollection *targets = query_overwrite_targets(seqbasep, transformed_strips); - SeqCollection *strips_to_delete = SEQ_collection_create(__func__); - - Sequence *target; - Sequence *transformed; - SEQ_ITERATOR_FOREACH (target, targets) { - SEQ_ITERATOR_FOREACH (transformed, transformed_strips) { - if (transformed->machine != target->machine) { - continue; - } - - const eOvelapDescrition overlap = overlap_description_get(transformed, target); - - if (overlap == STRIP_OVERLAP_IS_FULL) { - SEQ_collection_append_strip(target, strips_to_delete); - } - else if (overlap == STRIP_OVERLAP_IS_INSIDE) { - seq_transform_handle_overwrite_split(scene, seqbasep, transformed, target); - } - else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { - seq_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap); - } - } - } - - SEQ_collection_free(targets); - - /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()` - * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */ - if (SEQ_collection_len(strips_to_delete) > 0) { - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips_to_delete) { - SEQ_edit_flag_for_removal(scene, seqbasep, seq); - } - SEQ_edit_remove_flagged_sequences(scene, seqbasep); - } - SEQ_collection_free(strips_to_delete); -} - -static void seq_transform_handle_overlap_shuffle(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) -{ - ListBase *markers = &scene->markers; - - /* Shuffle non strips with no effects attached. */ - SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); - SEQ_transform_seqbase_shuffle_time( - standalone_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(standalone_strips); -} - -void SEQ_transform_handle_overlap(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) +static bool seq_transform_check_overlap(SeqCollection *transformed_strips) { - const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene); - - switch (overlap_mode) { - case SEQ_OVERLAP_EXPAND: - seq_transform_handle_expand_to_fit(scene, seqbasep, transformed_strips, use_sync_markers); - break; - case SEQ_OVERLAP_OVERWRITE: - seq_transform_handle_overwrite(scene, seqbasep, transformed_strips); - break; - case SEQ_OVERLAP_SHUFFLE: - seq_transform_handle_overlap_shuffle(scene, seqbasep, transformed_strips, use_sync_markers); - break; - } - - /* If any effects still overlap, we need to move them up. - * In some cases other strips can be overlapping still, see T90646. */ Sequence *seq; SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if (SEQ_transform_test_overlap(seqbasep, seq)) { - SEQ_transform_seqbase_shuffle(seqbasep, seq, scene); + if (seq->flag & SEQ_OVERLAP) { + return true; } - seq->flag &= ~SEQ_OVERLAP; } + return false; } static SeqCollection *seq_transform_collection_from_transdata(TransDataContainer *tc) @@ -607,12 +332,14 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c return; } + TransSeq *ts = tc->custom.type.data; ListBase *seqbasep = seqbase_active_get(t); Scene *scene = t->scene; const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; if (seq_transform_check_overlap(transformed_strips)) { - SEQ_transform_handle_overlap(scene, seqbasep, transformed_strips, use_sync_markers); + SEQ_transform_handle_overlap( + scene, seqbasep, transformed_strips, ts->time_dependent_strips, use_sync_markers); } SEQ_collection_free(transformed_strips); diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 5a28b8b7318..83b00da0446 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -613,7 +613,7 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c if (info.early_fragment_test_) { ss << "layout(early_fragment_tests) in;\n"; } - if (GLEW_VERSION_4_2 || GLEW_ARB_conservative_depth) { + if (GLEW_ARB_conservative_depth) { ss << "layout(" << to_string(info.depth_write_) << ") out float gl_FragDepth;\n"; } ss << "\n/* Outputs. */\n"; @@ -836,7 +836,7 @@ static char *glsl_patch_default_get() STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n"); STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n"); } - if (!GLEW_VERSION_4_2 && GLEW_ARB_conservative_depth) { + if (GLEW_ARB_conservative_depth) { STR_CONCAT(patch, slen, "#extension GL_ARB_conservative_depth : enable\n"); } if (GPU_shader_image_load_store_support()) { diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index 82bb1c57833..1fbddc45964 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -267,6 +267,11 @@ void AbstractHierarchyIterator::export_graph_construct() { Scene *scene = DEG_get_evaluated_scene(depsgraph_); + /* Add a "null" root node with no children immediately for the case where the top-most node in + * the scene is not being exported and a root node otherwise wouldn't get added. */ + ExportGraph::key_type root_node_id = ObjectIdentifier::for_real_object(nullptr); + export_graph_[root_node_id] = ExportChildren(); + DEG_OBJECT_ITER_BEGIN (depsgraph_, object, DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | diff --git a/source/blender/io/stl/importer/stl_import_mesh.cc b/source/blender/io/stl/importer/stl_import_mesh.cc index 5f5c2624414..7de8239b233 100644 --- a/source/blender/io/stl/importer/stl_import_mesh.cc +++ b/source/blender/io/stl/importer/stl_import_mesh.cc @@ -21,30 +21,30 @@ namespace blender::io::stl { -STLMeshHelper::STLMeshHelper(int num_tris, bool use_custom_normals) - : m_use_custom_normals(use_custom_normals) +STLMeshHelper::STLMeshHelper(int tris_num, bool use_custom_normals) + : use_custom_normals_(use_custom_normals) { - m_num_degenerate_tris = 0; - m_num_duplicate_tris = 0; - m_tris.reserve(num_tris); + degenerate_tris_num_ = 0; + duplicate_tris_num_ = 0; + tris_.reserve(tris_num); /* Upper bound (all vertices are unique). */ - m_verts.reserve(num_tris * 3); + verts_.reserve(tris_num * 3); if (use_custom_normals) { - m_loop_normals.reserve(num_tris * 3); + loop_normals_.reserve(tris_num * 3); } } bool STLMeshHelper::add_triangle(const float3 &a, const float3 &b, const float3 &c) { - int v1_id = m_verts.index_of_or_add(a); - int v2_id = m_verts.index_of_or_add(b); - int v3_id = m_verts.index_of_or_add(c); + int v1_id = verts_.index_of_or_add(a); + int v2_id = verts_.index_of_or_add(b); + int v3_id = verts_.index_of_or_add(c); if ((v1_id == v2_id) || (v1_id == v3_id) || (v2_id == v3_id)) { - m_num_degenerate_tris++; + degenerate_tris_num_++; return false; } - if (!m_tris.add({v1_id, v2_id, v3_id})) { - m_num_duplicate_tris++; + if (!tris_.add({v1_id, v2_id, v3_id})) { + duplicate_tris_num_++; return false; } return true; @@ -56,18 +56,18 @@ void STLMeshHelper::add_triangle(const float3 &a, const float3 &custom_normal) { if (add_triangle(a, b, c)) { - m_loop_normals.append_n_times(custom_normal, 3); + loop_normals_.append_n_times(custom_normal, 3); } } Mesh *STLMeshHelper::to_mesh(Main *bmain, char *mesh_name) { - if (m_num_degenerate_tris > 0) { - std::cout << "STL Importer: " << m_num_degenerate_tris << "degenerate triangles were removed" + if (degenerate_tris_num_ > 0) { + std::cout << "STL Importer: " << degenerate_tris_num_ << "degenerate triangles were removed" << std::endl; } - if (m_num_duplicate_tris > 0) { - std::cout << "STL Importer: " << m_num_duplicate_tris << "duplicate triangles were removed" + if (duplicate_tris_num_ > 0) { + std::cout << "STL Importer: " << duplicate_tris_num_ << "duplicate triangles were removed" << std::endl; } @@ -75,36 +75,36 @@ Mesh *STLMeshHelper::to_mesh(Main *bmain, char *mesh_name) /* User count is already 1 here, but will be set later in #BKE_mesh_assign_object. */ id_us_min(&mesh->id); - mesh->totvert = m_verts.size(); + mesh->totvert = verts_.size(); mesh->mvert = static_cast<MVert *>( CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, nullptr, mesh->totvert)); for (int i = 0; i < mesh->totvert; i++) { - copy_v3_v3(mesh->mvert[i].co, m_verts[i]); + copy_v3_v3(mesh->mvert[i].co, verts_[i]); } - mesh->totpoly = m_tris.size(); - mesh->totloop = m_tris.size() * 3; + mesh->totpoly = tris_.size(); + mesh->totloop = tris_.size() * 3; mesh->mpoly = static_cast<MPoly *>( CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, nullptr, mesh->totpoly)); mesh->mloop = static_cast<MLoop *>( CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, nullptr, mesh->totloop)); - threading::parallel_for(m_tris.index_range(), 2048, [&](IndexRange tris_range) { + threading::parallel_for(tris_.index_range(), 2048, [&](IndexRange tris_range) { for (const int i : tris_range) { mesh->mpoly[i].loopstart = 3 * i; mesh->mpoly[i].totloop = 3; - mesh->mloop[3 * i].v = m_tris[i].v1; - mesh->mloop[3 * i + 1].v = m_tris[i].v2; - mesh->mloop[3 * i + 2].v = m_tris[i].v3; + mesh->mloop[3 * i].v = tris_[i].v1; + mesh->mloop[3 * i + 1].v = tris_[i].v2; + mesh->mloop[3 * i + 2].v = tris_[i].v3; } }); /* NOTE: edges must be calculated first before setting custom normals. */ BKE_mesh_calc_edges(mesh, false, false); - if (m_use_custom_normals && m_loop_normals.size() == mesh->totloop) { - BKE_mesh_set_custom_normals(mesh, reinterpret_cast<float(*)[3]>(m_loop_normals.data())); + if (use_custom_normals_ && loop_normals_.size() == mesh->totloop) { + BKE_mesh_set_custom_normals(mesh, reinterpret_cast<float(*)[3]>(loop_normals_.data())); mesh->flag |= ME_AUTOSMOOTH; } diff --git a/source/blender/io/stl/importer/stl_import_mesh.hh b/source/blender/io/stl/importer/stl_import_mesh.hh index a01e5be09a4..f1c0d2126a9 100644 --- a/source/blender/io/stl/importer/stl_import_mesh.hh +++ b/source/blender/io/stl/importer/stl_import_mesh.hh @@ -47,15 +47,15 @@ class Triangle { class STLMeshHelper { private: - VectorSet<float3> m_verts; - VectorSet<Triangle> m_tris; - Vector<float3> m_loop_normals; - int m_num_degenerate_tris; - int m_num_duplicate_tris; - const bool m_use_custom_normals; + VectorSet<float3> verts_; + VectorSet<Triangle> tris_; + Vector<float3> loop_normals_; + int degenerate_tris_num_; + int duplicate_tris_num_; + const bool use_custom_normals_; public: - STLMeshHelper(int num_tris, bool use_custom_normals); + STLMeshHelper(int tris_num, bool use_custom_normals); /* Creates a new triangle from specified vertex locations, * duplicate vertices and triangles are merged. diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 0a78cdc714d..a719dff2126 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -45,6 +45,7 @@ struct OBJExportParams { eEvaluationMode export_eval_mode; bool export_uv; bool export_normals; + bool export_colors; bool export_materials; bool export_triangulated_mesh; bool export_curves_as_nurbs; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 11d1bafdafe..cb95c561547 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -8,7 +8,9 @@ #include <cstdio> #include "BKE_blender_version.h" +#include "BKE_geometry_set.hh" +#include "BLI_color.hh" #include "BLI_enumerable_thread_specific.hh" #include "BLI_path_util.h" #include "BLI_task.hh" @@ -241,13 +243,38 @@ void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh, } void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, - const OBJMesh &obj_mesh_data) const + const OBJMesh &obj_mesh_data, + bool write_colors) const { const int tot_count = obj_mesh_data.tot_vertices(); - obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { - float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); - buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]); - }); + + Mesh *mesh = obj_mesh_data.get_mesh(); + CustomDataLayer *colors_layer = nullptr; + if (write_colors) { + colors_layer = BKE_id_attributes_active_color_get(&mesh->id); + } + if (write_colors && (colors_layer != nullptr)) { + MeshComponent component; + component.replace(mesh, GeometryOwnershipType::ReadOnly); + VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>( + colors_layer->name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f, 0.0f}); + + BLI_assert(tot_count == attribute.size()); + obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); + ColorGeometry4f linear = attribute.get(i); + float srgb[3]; + linearrgb_to_srgb_v3_v3(srgb, linear); + buf.write<eOBJSyntaxElement::vertex_coords_color>( + vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]); + }); + } + else { + obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); + buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]); + }); + } } void OBJWriter::write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &r_obj_mesh_data) const diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 77da7b44276..97c23484426 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -72,9 +72,11 @@ class OBJWriter : NonMovable, NonCopyable { */ void write_mtllib_name(const StringRefNull mtl_filepath) const; /** - * Write vertex coordinates for all vertices as "v x y z". + * Write vertex coordinates for all vertices as "v x y z" or "v x y z r g b". */ - void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const; + void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, + const OBJMesh &obj_mesh_data, + bool write_colors) const; /** * Write UV vertex coordinates for all vertices as `vt u v`. * \note UV indices are stored here, but written with polygons later. diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index f0263989bfc..157d7760307 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -30,6 +30,7 @@ enum class eFileType { enum class eOBJSyntaxElement { vertex_coords, + vertex_coords_color, uv_vertex_coords, normal, poly_element_begin, @@ -130,6 +131,9 @@ constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key case eOBJSyntaxElement::vertex_coords: { return {"v {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>}; } + case eOBJSyntaxElement::vertex_coords_color: { + return {"v {:.6f} {:.6f} {:.6f} {:.6f} {:.6f} {:.6f}\n", 6, is_type_float<T...>}; + } case eOBJSyntaxElement::uv_vertex_coords: { return {"vt {:.6f} {:.6f}\n", 2, is_type_float<T...>}; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index 91213ec8152..ee2e6227700 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -241,6 +241,11 @@ class OBJMesh : NonCopyable { return i < 0 || i >= poly_order_.size() ? i : poly_order_[i]; } + Mesh *get_mesh() const + { + return export_mesh_eval_; + } + private: /** * Free the mesh if _the exporter_ created it. diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index b6e636b389d..b0938084efb 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -195,7 +195,7 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me auto &fh = buffers[i]; obj_writer.write_object_name(fh, obj); - obj_writer.write_vertex_coords(fh, obj); + obj_writer.write_vertex_coords(fh, obj, export_params.export_colors); if (obj.tot_polygons() > 0) { if (export_params.export_smooth_groups) { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index a627e7261e3..9cfce5c2257 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -5,12 +5,15 @@ */ #include "BLI_map.hh" +#include "BLI_math_color.h" #include "BLI_string_ref.hh" #include "BLI_vector.hh" #include "obj_import_file_reader.hh" #include "obj_import_string_utils.hh" +#include <charconv> + namespace blender::io::obj { using std::string; @@ -34,6 +37,7 @@ static Geometry *create_geometry(Geometry *const prev_geometry, g->geom_type_ = new_type; g->geometry_name_ = name.is_empty() ? "New object" : name; g->vertex_start_ = global_vertices.vertices.size(); + g->vertex_color_start_ = global_vertices.vertex_colors.size(); r_offset.set_index_offset(g->vertex_start_); return g; }; @@ -71,9 +75,51 @@ static void geom_add_vertex(Geometry *geom, GlobalVertices &r_global_vertices) { float3 vert; - parse_floats(p, end, 0.0f, vert, 3); + p = parse_floats(p, end, 0.0f, vert, 3); r_global_vertices.vertices.append(vert); geom->vertex_count_++; + /* OBJ extension: "xyzrgb" vertex colors, when the vertex position + * is followed by 3 more RGB color components. See + * http://paulbourke.net/dataformats/obj/colour.html */ + if (p < end) { + float3 srgb; + p = parse_floats(p, end, -1.0f, srgb, 3); + if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) { + float3 linear; + srgb_to_linearrgb_v3_v3(linear, srgb); + r_global_vertices.vertex_colors.append(linear); + geom->vertex_color_count_++; + } + } +} + +static void geom_add_mrgb_colors(Geometry *geom, + const char *p, + const char *end, + GlobalVertices &r_global_vertices) +{ + /* MRGB color extension, in the form of + * "#MRGB MMRRGGBBMMRRGGBB ..." + * http://paulbourke.net/dataformats/obj/colour.html */ + p = drop_whitespace(p, end); + const int mrgb_length = 8; + while (p + mrgb_length <= end) { + uint32_t value = 0; + std::from_chars_result res = std::from_chars(p, p + mrgb_length, value, 16); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + return; + } + unsigned char srgb[4]; + srgb[0] = (value >> 16) & 0xFF; + srgb[1] = (value >> 8) & 0xFF; + srgb[2] = value & 0xFF; + srgb[3] = 0xFF; + float linear[4]; + srgb_to_linearrgb_uchar4(linear, srgb); + r_global_vertices.vertex_colors.append({linear[0], linear[1], linear[2]}); + geom->vertex_color_count_++; + p += mrgb_length; + } } static void geom_add_vertex_normal(Geometry *geom, @@ -149,7 +195,7 @@ static void geom_add_polygon(Geometry *geom, if (p < end && *p == '/') { ++p; p = parse_int(p, end, INT32_MAX, corner.vertex_normal_index, false); - got_normal = corner.uv_vert_index != INT32_MAX; + got_normal = corner.vertex_normal_index != INT32_MAX; } } /* Always keep stored indices non-negative and zero-based. */ @@ -172,7 +218,10 @@ static void geom_add_polygon(Geometry *geom, face_valid = false; } } - if (got_normal) { + /* Ignore corner normal index, if the geometry does not have any normals. + * Some obj files out there do have face definitions that refer to normal indices, + * without any normals being present (T98782). */ + if (got_normal && geom->has_vertex_normals_) { corner.vertex_normal_index += corner.vertex_normal_index < 0 ? global_vertices.vertex_normals.size() : -1; @@ -482,6 +531,9 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, else if (parse_keyword(p, end, "mtllib")) { add_mtl_library(StringRef(p, end).trim()); } + else if (parse_keyword(p, end, "#MRGB")) { + geom_add_mrgb_colors(curr_geom, p, end, r_global_vertices); + } /* Comments. */ else if (*p == '#') { /* Nothing to do. */ diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 854ab96e39f..acc35ad46e1 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -8,6 +8,7 @@ #include "DNA_mesh_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_material.h" #include "BKE_mesh.h" @@ -50,6 +51,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, create_edges(mesh); create_uv_verts(mesh); create_normals(mesh); + create_colors(mesh); create_materials(bmain, materials, created_materials, obj); if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) { @@ -345,4 +347,26 @@ void MeshFromGeometry::create_normals(Mesh *mesh) MEM_freeN(loop_normals); } +void MeshFromGeometry::create_colors(Mesh *mesh) +{ + /* Nothing to do if we don't have vertex colors. */ + if (mesh_geometry_.vertex_color_count_ < 1) { + return; + } + if (mesh_geometry_.vertex_color_count_ != mesh_geometry_.vertex_count_) { + std::cerr << "Mismatching number of vertices (" << mesh_geometry_.vertex_count_ + << ") and colors (" << mesh_geometry_.vertex_color_count_ << ") on object '" + << mesh_geometry_.geometry_name_ << "', ignoring colors." << std::endl; + return; + } + + CustomDataLayer *color_layer = BKE_id_attribute_new( + &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); + float4 *colors = (float4 *)color_layer->data; + for (int i = 0; i < mesh_geometry_.vertex_color_count_; ++i) { + float3 c = global_vertices_.vertex_colors[mesh_geometry_.vertex_color_start_ + i]; + colors[i] = float4(c.x, c.y, c.z, 1.0f); + } +} + } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh index cf4a2aee394..216717f3578 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh @@ -65,6 +65,7 @@ class MeshFromGeometry : NonMovable, NonCopyable { Map<std::string, Material *> &created_materials, Object *obj); void create_normals(Mesh *mesh); + void create_colors(Mesh *mesh); }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh index b67ba46af03..69babc26bb0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -26,6 +26,7 @@ struct GlobalVertices { Vector<float3> vertices; Vector<float2> uv_vertices; Vector<float3> vertex_normals; + Vector<float3> vertex_colors; }; /** @@ -102,6 +103,8 @@ struct Geometry { int vertex_start_ = 0; int vertex_count_ = 0; + int vertex_color_start_ = 0; + int vertex_color_count_ = 0; /** Edges written in the file in addition to (or even without polygon) elements. */ Vector<MEdge> edges_; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index b384d934c82..fea74c2fd90 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -436,6 +436,22 @@ TEST_F(obj_exporter_regression_test, cubes_positioned) _export.params); } +/* Disabled until sRGB<->Linear conversion produces close enough results between + * SSE2 and non-SSE2. +TEST_F(obj_exporter_regression_test, cubes_vertex_colors) +{ + OBJExportParamsDefault _export; + _export.params.export_colors = true; + _export.params.export_normals = false; + _export.params.export_uv = false; + _export.params.export_materials = false; + compare_obj_export_to_golden("io_tests/blend_geometry/cubes_vertex_colors.blend", + "io_tests/obj/cubes_vertex_colors.obj", + "", + _export.params); +} +*/ + TEST_F(obj_exporter_regression_test, cubes_with_textures_strip) { OBJExportParamsDefault _export; @@ -494,6 +510,7 @@ TEST_F(obj_exporter_regression_test, all_objects) _export.params.forward_axis = IO_AXIS_Y; _export.params.up_axis = IO_AXIS_Z; _export.params.export_smooth_groups = true; + //_export.params.export_colors = true; compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend", "io_tests/obj/all_objects.obj", "io_tests/obj/all_objects.mtl", diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 42f7620999d..7d3b41ed527 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -26,6 +26,7 @@ struct OBJExportParamsDefault { params.export_selected_objects = false; params.export_uv = true; params.export_normals = true; + params.export_colors = false; params.export_materials = true; params.path_mode = PATH_REFERENCE_AUTO; params.export_triangulated_mesh = false; diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 6d09f522028..efdd8839615 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -39,6 +39,7 @@ struct Expectation { float3 vert_first, vert_last; float3 normal_first; float2 uv_first; + float4 color_first = {-1, -1, -1, -1}; }; class obj_importer_test : public BlendfileLoadingBaseTest { @@ -98,6 +99,15 @@ class obj_importer_test : public BlendfileLoadingBaseTest { CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); float2 uv_first = mloopuv ? float2(mloopuv->uv) : float2(0, 0); EXPECT_V2_NEAR(uv_first, exp.uv_first, 0.0001f); + if (exp.color_first.x >= 0) { + const float4 *colors = (const float4 *)(CustomData_get_layer(&mesh->vdata, + CD_PROP_COLOR)); + EXPECT_TRUE(colors != nullptr); + EXPECT_V4_NEAR(colors[0], exp.color_first, 0.0001f); + } + else { + EXPECT_FALSE(CustomData_has_layer(&mesh->vdata, CD_PROP_COLOR)); + } } if (object->type == OB_CURVES_LEGACY) { Curve *curve = static_cast<Curve *>(DEG_get_evaluated_object(depsgraph, object)->data); @@ -434,7 +444,16 @@ TEST_F(obj_importer_test, import_all_objects) float3(16, 1, -1), float3(14, 1, 1), float3(0, 0, 1)}, - {"OBVColCube", OB_MESH, 8, 13, 7, 26, float3(13, 1, -1), float3(11, 1, 1), float3(0, 0, 1)}, + {"OBVColCube", + OB_MESH, + 8, + 13, + 7, + 26, + float3(13, 1, -1), + float3(11, 1, 1), + float3(0, 0, 1), + float2(0, 0)}, {"OBUVCube", OB_MESH, 8, @@ -490,4 +509,105 @@ TEST_F(obj_importer_test, import_all_objects) import_and_check("all_objects.obj", expect, std::size(expect), 7); } +/* +TEST_F(obj_importer_test, import_cubes_vertex_colors) +{ + Expectation expect[] = { + {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + {"OBCubeVertexByte", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1.0f, 1.0f, -1.0f), + float3(-1.0f, -1.0f, 1.0f), + float3(0, 0, 0), + float2(0, 0), + float4(0.846873f, 0.027321f, 0.982251f, 1.0f)}, + {"OBCubeVertexFloat", + OB_MESH, + 8, + 12, + 6, + 24, + float3(3.392028f, 1.0f, -1.0f), + float3(1.392028f, -1.0f, 1.0f), + float3(0, 0, 0), + float2(0, 0), + float4(49.99558f, 0.027321f, 0.982251f, 1.0f)}, + {"OBCubeCornerByte", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1.0f, 1.0f, -3.812445f), + float3(-1.0f, -1.0f, -1.812445f), + float3(0, 0, 0), + float2(0, 0), + float4(0.89627f, 0.036889f, 0.47932f, 1.0f)}, + {"OBCubeCornerFloat", + OB_MESH, + 8, + 12, + 6, + 24, + float3(3.481967f, 1.0f, -3.812445f), + float3(1.481967f, -1.0f, -1.812445f), + float3(0, 0, 0), + float2(0, 0), + float4(1.564582f, 0.039217f, 0.664309f, 1.0f)}, + {"OBCubeMultiColorAttribs", + OB_MESH, + 8, + 12, + 6, + 24, + float3(-4.725068f, -1.0f, 1.0f), + float3(-2.725068f, 1.0f, -1.0f), + float3(0, 0, 0), + float2(0, 0), + float4(0.270498f, 0.47932f, 0.262251f, 1.0f)}, + {"OBCubeNoColors", + OB_MESH, + 8, + 12, + 6, + 24, + float3(-4.550208f, -1.0f, -1.918042f), + float3(-2.550208f, 1.0f, -3.918042f)}, + }; + import_and_check("cubes_vertex_colors.obj", expect, std::size(expect), 0); +} + */ + +TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb) +{ + Expectation expect[] = {{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + {"OBCubeXYZRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1, 1, -1), + float3(-1, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.6038f, 0.3185f, 0.1329f, 1.0f)}, + {"OBCubeMRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(4, 1, -1), + float3(2, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}}; + import_and_check("cubes_vertex_colors_mrgb.obj", expect, std::size(expect), 0); +} + } // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index a66badb4a68..2eca84959b8 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -32,6 +32,7 @@ struct MVert; struct Material; struct Mesh; struct SubdivCCG; +struct SubsurfRuntimeData; # # @@ -123,26 +124,18 @@ typedef struct Mesh_Runtime { /** * Settings for lazily evaluating the subdivision on the CPU if needed. These are - * set in the modifier when GPU subdivision can be performed. + * set in the modifier when GPU subdivision can be performed, and owned by the by + * the modifier in the object. */ - SessionUUID subsurf_session_uuid; - char subsurf_resolution; - char subsurf_do_loop_normals; - char subsurf_apply_render; - char subsurf_use_optimal_display; - - /* Cached from the draw code for stats display. */ - int subsurf_totvert; - int subsurf_totedge; - int subsurf_totpoly; - int subsurf_totloop; - char _pad2[2]; + struct SubsurfRuntimeData *subsurf_runtime_data; + void *_pad1; /** * Caches for lazily computed vertex and polygon normals. These are stored here rather than in * #CustomData because they can be calculated on a const mesh, and adding custom data layers on a * const mesh is not thread-safe. */ + char _pad2[6]; char vert_normals_dirty; char poly_normals_dirty; float (*vert_normals)[3]; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 6e3ce7e98a8..f148116eba8 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -178,13 +178,6 @@ typedef enum { SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS = 1, } eSubsurfBoundarySmooth; -typedef struct SubsurfRuntimeData { - /* Cached subdivision surface descriptor, with topology and settings. */ - struct Subdiv *subdiv; - char set_by_draw_code; - char _pad[7]; -} SubsurfRuntimeData; - typedef struct SubsurfModifierData { ModifierData modifier; diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index 4201bab0326..23f447f2469 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -196,16 +196,21 @@ static Mesh *subdiv_as_ccg(SubsurfModifierData *smd, /* Cache settings for lazy CPU evaluation. */ -static void subdiv_cache_cpu_evaluation_settings(const ModifierEvalContext *ctx, - Mesh *me, - SubsurfModifierData *smd) +static void subdiv_cache_mesh_wrapper_settings(const ModifierEvalContext *ctx, + Mesh *mesh, + SubsurfModifierData *smd, + SubsurfRuntimeData *runtime_data) { SubdivToMeshSettings mesh_settings; subdiv_mesh_settings_init(&mesh_settings, smd, ctx); - me->runtime.subsurf_apply_render = (ctx->flag & MOD_APPLY_RENDER) != 0; - me->runtime.subsurf_resolution = mesh_settings.resolution; - me->runtime.subsurf_use_optimal_display = mesh_settings.use_optimal_display; - me->runtime.subsurf_session_uuid = smd->modifier.session_uuid; + + runtime_data->has_gpu_subdiv = true; + runtime_data->resolution = mesh_settings.resolution; + runtime_data->use_optimal_display = mesh_settings.use_optimal_display; + runtime_data->calc_loop_normals = false; /* Set at the end of modifier stack evaluation. */ + runtime_data->use_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals); + + mesh->runtime.subsurf_runtime_data = runtime_data; } /* Modifier itself. */ @@ -218,13 +223,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return result; #endif SubsurfModifierData *smd = (SubsurfModifierData *)md; - SubdivSettings subdiv_settings; - BKE_subsurf_modifier_subdiv_settings_init( - &subdiv_settings, smd, (ctx->flag & MOD_APPLY_RENDER) != 0); - if (subdiv_settings.level == 0) { + if (!BKE_subsurf_modifier_runtime_init(smd, (ctx->flag & MOD_APPLY_RENDER) != 0)) { return result; } - SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); + + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; /* Delay evaluation to the draw code if possible, provided we do not have to apply the modifier. */ @@ -237,13 +240,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * const bool is_editmode = (mesh->edit_mesh != NULL); const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode); if (BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ctx->object, mesh, smd, required_mode)) { - subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd); + subdiv_cache_mesh_wrapper_settings(ctx, mesh, smd, runtime_data); return result; } } - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure( - smd, &subdiv_settings, mesh, false); + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, mesh, false); if (subdiv == NULL) { /* Happens on bad topology, but also on empty input mesh. */ return result; @@ -294,15 +296,11 @@ static void deformMatrices(ModifierData *md, (void)deform_matrices; SubsurfModifierData *smd = (SubsurfModifierData *)md; - SubdivSettings subdiv_settings; - BKE_subsurf_modifier_subdiv_settings_init( - &subdiv_settings, smd, (ctx->flag & MOD_APPLY_RENDER) != 0); - if (subdiv_settings.level == 0) { + if (!BKE_subsurf_modifier_runtime_init(smd, (ctx->flag & MOD_APPLY_RENDER) != 0)) { return; } - SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure( - smd, &subdiv_settings, mesh, false); + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, mesh, false); if (subdiv == NULL) { /* Happens on bad topology, but also on empty input mesh. */ return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc index c400760f902..532c3dc81e5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -65,7 +65,7 @@ class PlanarFieldInput final : public GeometryFieldInput { float3 reference_normal = poly_normals[i_poly]; float min = FLT_MAX; - float max = FLT_MIN; + float max = -FLT_MAX; for (const int i_loop : poly_loops.index_range()) { const float3 vert = mesh->mvert[poly_loops[i_loop].v].co; diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 93ce6873d3b..31a7399f844 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -46,6 +46,7 @@ bool SEQ_transform_seqbase_shuffle(struct ListBase *seqbasep, struct Sequence *test, struct Scene *evil_scene); bool SEQ_transform_seqbase_shuffle_time(struct SeqCollection *strips_to_shuffle, + struct SeqCollection *time_dependent_strips, struct ListBase *seqbasep, struct Scene *evil_scene, struct ListBase *markers, @@ -54,6 +55,7 @@ bool SEQ_transform_seqbase_shuffle_time(struct SeqCollection *strips_to_shuffle, void SEQ_transform_handle_overlap(struct Scene *scene, struct ListBase *seqbasep, struct SeqCollection *transformed_strips, + struct SeqCollection *time_dependent_strips, bool use_sync_markers); /** * Check if the selected seq's reference unselected seq's. diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index ebf317eff05..19caf12ff2a 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -13,11 +13,13 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BKE_main.h" #include "BKE_scene.h" #include "BKE_sound.h" #include "SEQ_animation.h" #include "SEQ_channels.h" +#include "SEQ_edit.h" #include "SEQ_effects.h" #include "SEQ_iterator.h" #include "SEQ_relations.h" @@ -307,6 +309,7 @@ static int shuffle_seq_time_offset_get(SeqCollection *strips_to_shuffle, } bool SEQ_transform_seqbase_shuffle_time(SeqCollection *strips_to_shuffle, + SeqCollection *time_dependent_strips, ListBase *seqbasep, Scene *evil_scene, ListBase *markers, @@ -323,6 +326,12 @@ bool SEQ_transform_seqbase_shuffle_time(SeqCollection *strips_to_shuffle, seq->flag &= ~SEQ_OVERLAP; } + if (time_dependent_strips != NULL) { + SEQ_ITERATOR_FOREACH (seq, time_dependent_strips) { + SEQ_offset_animdata(evil_scene, seq, offset); + } + } + if (use_sync_markers && !(evil_scene->toolsettings->lock_markers) && (markers != NULL)) { TimeMarker *marker; /* affect selected markers - it's unlikely that we will want to affect all in this way? */ @@ -337,6 +346,296 @@ bool SEQ_transform_seqbase_shuffle_time(SeqCollection *strips_to_shuffle, return offset ? false : true; } +static SeqCollection *extract_standalone_strips(SeqCollection *transformed_strips) +{ + SeqCollection *collection = SEQ_collection_create(__func__); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == NULL) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} + +/* Query strips positioned after left edge of transformed strips bound-box. */ +static SeqCollection *query_right_side_strips(ListBase *seqbase, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips) +{ + int minframe = MAXFRAME; + { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + minframe = min_ii(minframe, SEQ_time_left_handle_frame_get(seq)); + } + } + + SeqCollection *collection = SEQ_collection_create(__func__); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (SEQ_collection_has_strip(seq, time_dependent_strips)) { + continue; + } + if (SEQ_collection_has_strip(seq, transformed_strips)) { + continue; + } + + if ((seq->flag & SELECT) == 0 && SEQ_time_left_handle_frame_get(seq) >= minframe) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} + +/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal + * to overlap of transformed strips. */ +static void seq_transform_handle_expand_to_fit(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips, + bool use_sync_markers) +{ + ListBase *markers = &scene->markers; + + SeqCollection *right_side_strips = query_right_side_strips( + seqbasep, transformed_strips, time_dependent_strips); + + /* Temporarily move right side strips beyond timeline boundary. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, right_side_strips) { + seq->machine += MAXSEQ * 2; + } + + /* Shuffle transformed standalone strips. This is because transformed strips can overlap with + * strips on left side. */ + SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); + SEQ_transform_seqbase_shuffle_time( + standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers); + SEQ_collection_free(standalone_strips); + + /* Move temporarily moved strips back to their original place and tag for shuffling. */ + SEQ_ITERATOR_FOREACH (seq, right_side_strips) { + seq->machine -= MAXSEQ * 2; + } + /* Shuffle again to displace strips on right side. Final effect shuffling is done in + * SEQ_transform_handle_overlap. */ + SEQ_transform_seqbase_shuffle_time( + right_side_strips, NULL, seqbasep, scene, markers, use_sync_markers); + SEQ_collection_free(right_side_strips); +} + +static SeqCollection *query_overwrite_targets(ListBase *seqbasep, + SeqCollection *transformed_strips) +{ + SeqCollection *collection = SEQ_query_unselected_strips(seqbasep); + + Sequence *seq, *seq_transformed; + SEQ_ITERATOR_FOREACH (seq, collection) { + bool does_overlap = false; + + SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) { + /* Effects of transformed strips can be unselected. These must not be included. */ + if (seq == seq_transformed) { + SEQ_collection_remove_strip(seq, collection); + } + if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) { + does_overlap = true; + } + } + + if (!does_overlap) { + SEQ_collection_remove_strip(seq, collection); + } + } + + return collection; +} + +typedef enum eOvelapDescrition { + /* No overlap. */ + STRIP_OVERLAP_NONE, + /* Overlapping strip covers overlapped completely. */ + STRIP_OVERLAP_IS_FULL, + /* Overlapping strip is inside overlapped. */ + STRIP_OVERLAP_IS_INSIDE, + /* Partial overlap between 2 strips. */ + STRIP_OVERLAP_LEFT_SIDE, + STRIP_OVERLAP_RIGHT_SIDE, +} eOvelapDescrition; + +static eOvelapDescrition overlap_description_get(const Sequence *transformed, + const Sequence *target) +{ + if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && + SEQ_time_right_handle_frame_get(transformed) >= SEQ_time_right_handle_frame_get(target)) { + return STRIP_OVERLAP_IS_FULL; + } + if (SEQ_time_left_handle_frame_get(transformed) > SEQ_time_left_handle_frame_get(target) && + SEQ_time_right_handle_frame_get(transformed) < SEQ_time_right_handle_frame_get(target)) { + return STRIP_OVERLAP_IS_INSIDE; + } + if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && + SEQ_time_left_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { + return STRIP_OVERLAP_LEFT_SIDE; + } + if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_right_handle_frame_get(target) && + SEQ_time_right_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { + return STRIP_OVERLAP_RIGHT_SIDE; + } + return STRIP_OVERLAP_NONE; +} + +/* Split strip in 3 parts, remove middle part and fit transformed inside. */ +static void seq_transform_handle_overwrite_split(Scene *scene, + ListBase *seqbasep, + const Sequence *transformed, + Sequence *target) +{ + /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can pass + * NULL here. */ + Main *bmain = NULL; + + Sequence *split_strip = SEQ_edit_strip_split(bmain, + scene, + seqbasep, + target, + SEQ_time_left_handle_frame_get(transformed), + SEQ_SPLIT_SOFT, + NULL); + SEQ_edit_strip_split(bmain, + scene, + seqbasep, + split_strip, + SEQ_time_right_handle_frame_get(transformed), + SEQ_SPLIT_SOFT, + NULL); + SEQ_edit_flag_for_removal(scene, seqbasep, split_strip); + SEQ_edit_remove_flagged_sequences(scene, seqbasep); +} + +/* Trim strips by adjusting handle position. + * This is bit more complicated in case overlap happens on effect. */ +static void seq_transform_handle_overwrite_trim(Scene *scene, + ListBase *seqbasep, + const Sequence *transformed, + Sequence *target, + const eOvelapDescrition overlap) +{ + SeqCollection *targets = SEQ_query_by_reference(target, seqbasep, SEQ_query_strip_effect_chain); + + /* Expand collection by adding all target's children, effects and their children. */ + if ((target->type & SEQ_TYPE_EFFECT) != 0) { + SEQ_collection_expand(seqbasep, targets, SEQ_query_strip_effect_chain); + } + + /* Trim all non effects, that have influence on effect length which is overlapping. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, targets) { + if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) { + continue; + } + if (overlap == STRIP_OVERLAP_LEFT_SIDE) { + SEQ_time_left_handle_frame_set(scene, seq, SEQ_time_right_handle_frame_get(transformed)); + } + else { + BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); + SEQ_time_right_handle_frame_set(scene, seq, SEQ_time_left_handle_frame_get(transformed)); + } + } + SEQ_collection_free(targets); +} + +static void seq_transform_handle_overwrite(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips) +{ + SeqCollection *targets = query_overwrite_targets(seqbasep, transformed_strips); + SeqCollection *strips_to_delete = SEQ_collection_create(__func__); + + Sequence *target; + Sequence *transformed; + SEQ_ITERATOR_FOREACH (target, targets) { + SEQ_ITERATOR_FOREACH (transformed, transformed_strips) { + if (transformed->machine != target->machine) { + continue; + } + + const eOvelapDescrition overlap = overlap_description_get(transformed, target); + + if (overlap == STRIP_OVERLAP_IS_FULL) { + SEQ_collection_append_strip(target, strips_to_delete); + } + else if (overlap == STRIP_OVERLAP_IS_INSIDE) { + seq_transform_handle_overwrite_split(scene, seqbasep, transformed, target); + } + else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { + seq_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap); + } + } + } + + SEQ_collection_free(targets); + + /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()` + * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */ + if (SEQ_collection_len(strips_to_delete) > 0) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips_to_delete) { + SEQ_edit_flag_for_removal(scene, seqbasep, seq); + } + SEQ_edit_remove_flagged_sequences(scene, seqbasep); + } + SEQ_collection_free(strips_to_delete); +} + +static void seq_transform_handle_overlap_shuffle(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips, + bool use_sync_markers) +{ + ListBase *markers = &scene->markers; + + /* Shuffle non strips with no effects attached. */ + SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); + SEQ_transform_seqbase_shuffle_time( + standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers); + SEQ_collection_free(standalone_strips); +} + +void SEQ_transform_handle_overlap(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips, + bool use_sync_markers) +{ + const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene); + + switch (overlap_mode) { + case SEQ_OVERLAP_EXPAND: + seq_transform_handle_expand_to_fit( + scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers); + break; + case SEQ_OVERLAP_OVERWRITE: + seq_transform_handle_overwrite(scene, seqbasep, transformed_strips); + break; + case SEQ_OVERLAP_SHUFFLE: + seq_transform_handle_overlap_shuffle( + scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers); + break; + } + + /* If any effects still overlap, we need to move them up. + * In some cases other strips can be overlapping still, see T90646. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if (SEQ_transform_test_overlap(seqbasep, seq)) { + SEQ_transform_seqbase_shuffle(seqbasep, seq, scene); + } + seq->flag &= ~SEQ_OVERLAP; + } +} + void SEQ_transform_offset_after_frame(Scene *scene, ListBase *seqbase, const int delta, diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index ac06ababfd4..60cded3b869 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -738,6 +738,7 @@ void WM_operator_last_properties_ensure(struct wmOperatorType *ot, struct Pointe wmOperator *WM_operator_last_redo(const struct bContext *C); /** * Use for drag & drop a path or name with operators invoke() function. + * Returns null if no operator property is set to identify the file or ID to use. */ ID *WM_operator_drop_load_path(struct bContext *C, struct wmOperator *op, short idcode); diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index c2a63c9db7a..63a7fb5ddaa 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -1153,9 +1153,17 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win) if (wm_software_cursor_needed()) { struct GrabState grab_state; - if (wm_software_cursor_needed_for_window(win, &grab_state) && - wm_software_cursor_motion_test(win)) { - return true; + if (wm_software_cursor_needed_for_window(win, &grab_state)) { + if (wm_software_cursor_motion_test(win)) { + return true; + } + } + else { + /* Detect the edge case when the previous draw used the software cursor but this one doesn't, + * it's important to redraw otherwise the software cursor will remain displayed. */ + if (g_software_cursor.winid != -1) { + return true; + } } } diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c index 531da3cf2e8..0817b10f86e 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.c +++ b/source/blender/windowmanager/intern/wm_keymap_utils.c @@ -270,7 +270,10 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) break; } } - else if (STRPREFIX(opname, "CURVES_SCULPT_OT")) { + else if (STRPREFIX(opname, "CURVES_OT")) { + km = WM_keymap_find_all(wm, "Curves", 0, 0); + } + else if (STRPREFIX(opname, "SCULPT_CURVES_OT")) { km = WM_keymap_find_all(wm, "Sculpt Curves", 0, 0); } else if (STRPREFIX(opname, "MBALL_OT")) { diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 7b768bd8c70..9f21e952850 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1308,6 +1308,10 @@ ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short i return id; } + if (!WM_operator_properties_id_lookup_is_set(op->ptr)) { + return NULL; + } + /* Lookup an already existing ID. */ id = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, idcode); diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index cae81af3144..15441918800 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -253,8 +253,11 @@ class Report: failed = len(failed_tests) > 0 if failed: message = """<div class="alert alert-danger" role="alert">""" - message += """Run this command to update reference images for failed tests, or create images for new tests:<br>""" - message += """<tt>BLENDER_TEST_UPDATE=1 ctest -R %s</tt>""" % self.title.lower() + message += """<p>Run this command to regenerate reference (ground truth) images:</p>""" + message += """<p><tt>BLENDER_TEST_UPDATE=1 ctest -R %s</tt></p>""" % self.title.lower() + message += """<p>This then happens for new and failing tests; reference images of """ \ + """passing test cases will not be updated. Be sure to commit the new reference """ \ + """images to the SVN repository afterwards.</p>""" message += """</div>""" else: message = "" @@ -294,6 +297,7 @@ class Report: background-position:0 0, 25px 0, 25px -25px, 0px 25px; }} table td:first-child {{ width: 256px; }} + p {{ margin-bottom: 0.5rem; }} </style> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> </head> |