Merge branch 'master' into temp-sculpt-roll-mapping
This commit is contained in:
commit
997ff01503
|
@ -257,6 +257,12 @@ if(UNIX AND NOT (APPLE OR HAIKU))
|
|||
|
||||
option(WITH_GHOST_WAYLAND_DYNLOAD "Enable runtime dynamic WAYLAND libraries loading" ON)
|
||||
mark_as_advanced(WITH_GHOST_WAYLAND_DYNLOAD)
|
||||
|
||||
set(WITH_GHOST_WAYLAND_APP_ID "" CACHE STRING "\
|
||||
The application ID used for Blender (use default when an empty string), \
|
||||
this can be used to differentiate Blender instances by version or branch for example."
|
||||
)
|
||||
mark_as_advanced(WITH_GHOST_WAYLAND_APP_ID)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -103,10 +103,6 @@ if(EXISTS ${SOURCE_DIR}/.git)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(MY_WC_BRANCH MATCHES "^blender-v")
|
||||
set(MY_WC_BRANCH "master")
|
||||
endif()
|
||||
|
||||
unset(_git_below_check)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -72,6 +72,11 @@ bool BlenderImageLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaDat
|
|||
metadata.colorspace = u_colorspace_raw;
|
||||
}
|
||||
else {
|
||||
/* In some cases (e.g. T94135), the colorspace setting in Blender gets updated as part of the
|
||||
* metadata queries in this function, so update the colorspace setting here. */
|
||||
PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
|
||||
metadata.colorspace = get_enum_identifier(colorspace_ptr, "name");
|
||||
|
||||
if (metadata.channels == 1) {
|
||||
metadata.type = IMAGE_DATA_TYPE_BYTE;
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ DenoiserGPU::~DenoiserGPU()
|
|||
}
|
||||
|
||||
bool DenoiserGPU::denoise_buffer(const BufferParams &buffer_params,
|
||||
RenderBuffers *render_buffers,
|
||||
const int num_samples,
|
||||
bool allow_inplace_modification)
|
||||
RenderBuffers *render_buffers,
|
||||
const int num_samples,
|
||||
bool allow_inplace_modification)
|
||||
{
|
||||
Device *denoiser_device = get_denoiser_device();
|
||||
if (!denoiser_device) {
|
||||
|
|
|
@ -393,6 +393,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
unset(INC_DST)
|
||||
|
||||
add_definitions(-DWITH_GHOST_WAYLAND)
|
||||
|
||||
if(NOT WITH_GHOST_WAYLAND_APP_ID STREQUAL "")
|
||||
add_definitions(-DWITH_GHOST_WAYLAND_APP_ID=${WITH_GHOST_WAYLAND_APP_ID})
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
|
|
|
@ -805,6 +805,21 @@ static GWL_SeatStatePointer *gwl_seat_state_pointer_from_cursor_surface(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static bool gwl_seat_key_depressed_suppress_warning(const GWL_Seat *seat)
|
||||
{
|
||||
bool suppress_warning = false;
|
||||
|
||||
#ifdef USE_GNOME_KEYBOARD_SUPPRESS_WARNING
|
||||
if ((seat->key_depressed_suppress_warning.any_mod_held == true) &&
|
||||
(seat->key_depressed_suppress_warning.any_keys_held_on_enter == false)) {
|
||||
/* The compositor gave us invalid information, don't show a warning. */
|
||||
suppress_warning = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return suppress_warning;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -1751,13 +1766,17 @@ static void keyboard_depressed_state_key_event(GWL_Seat *seat,
|
|||
const GHOST_TKey gkey,
|
||||
const GHOST_TEventType etype)
|
||||
{
|
||||
const bool show_warning = !gwl_seat_key_depressed_suppress_warning(seat);
|
||||
|
||||
if (GHOST_KEY_MODIFIER_CHECK(gkey)) {
|
||||
const int index = GHOST_KEY_MODIFIER_TO_INDEX(gkey);
|
||||
int16_t &value = seat->key_depressed.mods[index];
|
||||
if (etype == GHOST_kEventKeyUp) {
|
||||
value -= 1;
|
||||
if (UNLIKELY(value < 0)) {
|
||||
CLOG_WARN(LOG, "modifier (%d) has negative keys held (%d)!", index, value);
|
||||
if (show_warning) {
|
||||
CLOG_WARN(LOG, "modifier (%d) has negative keys held (%d)!", index, value);
|
||||
}
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
@ -2576,7 +2595,7 @@ static void pointer_handle_enter(void *data,
|
|||
|
||||
seat->system->seat_active_set(seat);
|
||||
|
||||
win->setCursorShape(win->getCursorShape());
|
||||
seat->system->cursor_shape_set(win->getCursorShape());
|
||||
|
||||
const wl_fixed_t scale = win->scale();
|
||||
seat->system->pushEvent_maybe_pending(
|
||||
|
@ -3238,7 +3257,7 @@ static void tablet_tool_handle_proximity_in(void *data,
|
|||
|
||||
win->activate();
|
||||
|
||||
win->setCursorShape(win->getCursorShape());
|
||||
seat->system->cursor_shape_set(win->getCursorShape());
|
||||
}
|
||||
static void tablet_tool_handle_proximity_out(void *data,
|
||||
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
|
||||
|
@ -3437,7 +3456,7 @@ static void tablet_tool_handle_frame(void *data,
|
|||
wl_fixed_to_int(scale * seat->tablet.xy[1]),
|
||||
tablet_tool->data));
|
||||
if (tablet_tool->proximity == false) {
|
||||
win->setCursorShape(win->getCursorShape());
|
||||
seat->system->cursor_shape_set(win->getCursorShape());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5514,14 +5533,7 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
|
|||
|
||||
const xkb_mod_mask_t state = xkb_state_serialize_mods(seat->xkb_state, XKB_STATE_MODS_DEPRESSED);
|
||||
|
||||
bool show_warning = true;
|
||||
#ifdef USE_GNOME_KEYBOARD_SUPPRESS_WARNING
|
||||
if ((seat->key_depressed_suppress_warning.any_mod_held == true) &&
|
||||
(seat->key_depressed_suppress_warning.any_keys_held_on_enter == false)) {
|
||||
/* The compositor gave us invalid information, don't show a warning. */
|
||||
show_warning = false;
|
||||
}
|
||||
#endif
|
||||
const bool show_warning = !gwl_seat_key_depressed_suppress_warning(seat);
|
||||
|
||||
/* Use local #GWL_KeyboardDepressedState to check which key is pressed.
|
||||
* Use XKB as the source of truth, if there is any discrepancy. */
|
||||
|
@ -5568,6 +5580,10 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
|
|||
|
||||
GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
GWL_Seat *seat = gwl_display_seat_active_get(display_);
|
||||
if (UNLIKELY(!seat)) {
|
||||
return GHOST_kFailure;
|
||||
|
@ -5796,6 +5812,10 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool selection) const
|
|||
|
||||
uint8_t GHOST_SystemWayland::getNumDisplays() const
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
return display_ ? uint8_t(display_->outputs.size()) : 0;
|
||||
}
|
||||
|
||||
|
@ -5914,7 +5934,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(const int32_t x, const int
|
|||
|
||||
void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
|
||||
{
|
||||
if (getNumDisplays() == 0) {
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
if (display_->outputs.empty()) {
|
||||
return;
|
||||
}
|
||||
/* We assume first output as main. */
|
||||
|
@ -5924,6 +5948,10 @@ void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &he
|
|||
|
||||
void GHOST_SystemWayland::getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
int32_t xy_min[2] = {INT32_MAX, INT32_MAX};
|
||||
int32_t xy_max[2] = {INT32_MIN, INT32_MIN};
|
||||
|
||||
|
@ -5947,6 +5975,7 @@ static GHOST_Context *createOffscreenContext_impl(GHOST_SystemWayland *system,
|
|||
struct wl_display *wl_display,
|
||||
wl_egl_window *egl_window)
|
||||
{
|
||||
/* Caller must lock `system->server_mutex`. */
|
||||
GHOST_Context *context;
|
||||
|
||||
for (int minor = 6; minor >= 0; --minor) {
|
||||
|
@ -6016,6 +6045,10 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g
|
|||
|
||||
GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
struct wl_surface *wl_surface = (struct wl_surface *)((GHOST_Context *)context)->getUserData();
|
||||
wl_egl_window *egl_window = (wl_egl_window *)wl_surface_get_user_data(wl_surface);
|
||||
wl_egl_window_destroy(egl_window);
|
||||
|
@ -6256,7 +6289,8 @@ static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_
|
|||
|
||||
GHOST_TSuccess GHOST_SystemWayland::cursor_shape_set(const GHOST_TStandardCursor shape)
|
||||
{
|
||||
/* No need to lock (caller must lock). */
|
||||
/* Caller must lock `server_mutex`. */
|
||||
|
||||
GWL_Seat *seat = gwl_display_seat_active_get(display_);
|
||||
if (UNLIKELY(!seat)) {
|
||||
return GHOST_kFailure;
|
||||
|
@ -6299,6 +6333,7 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_shape_set(const GHOST_TStandardCursor
|
|||
|
||||
GHOST_TSuccess GHOST_SystemWayland::cursor_shape_check(const GHOST_TStandardCursor cursorShape)
|
||||
{
|
||||
/* No need to lock `server_mutex`. */
|
||||
auto cursor_find = ghost_wl_cursors.find(cursorShape);
|
||||
if (cursor_find == ghost_wl_cursors.end()) {
|
||||
return GHOST_kFailure;
|
||||
|
@ -6678,6 +6713,8 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
wl_surface *wl_surface,
|
||||
const int scale)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
|
||||
/* Ignore, if the required protocols are not supported. */
|
||||
if (UNLIKELY(!display_->wp_relative_pointer_manager || !display_->wp_pointer_constraints)) {
|
||||
return GHOST_kFailure;
|
||||
|
|
|
@ -47,8 +47,6 @@ static constexpr size_t base_dpi = 96;
|
|||
# define use_libdecor GHOST_SystemWayland::use_libdecor_runtime()
|
||||
#endif
|
||||
|
||||
static GHOST_WindowManager *window_manager = nullptr;
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
struct WGL_LibDecor_Window {
|
||||
struct libdecor_frame *frame = nullptr;
|
||||
|
@ -115,7 +113,10 @@ struct GWL_Window {
|
|||
#endif
|
||||
WGL_XDG_Decor_Window *xdg_decor = nullptr;
|
||||
|
||||
/** The current value of frame, copied from `frame_pending` when applying updates. */
|
||||
/**
|
||||
* The current value of frame, copied from `frame_pending` when applying updates.
|
||||
* This avoids the need for locking when reading from `frame`.
|
||||
*/
|
||||
GWL_WindowFrame frame;
|
||||
GWL_WindowFrame frame_pending;
|
||||
|
||||
|
@ -170,35 +171,24 @@ static GHOST_TWindowState gwl_window_state_get(const GWL_Window *win)
|
|||
return GHOST_kWindowStateNormal;
|
||||
}
|
||||
|
||||
static bool gwl_window_state_set(GWL_Window *win, const GHOST_TWindowState state)
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
/**
|
||||
* \note Keep in sync with #gwl_window_state_set_for_xdg.
|
||||
*/
|
||||
static bool gwl_window_state_set_for_libdecor(struct libdecor_frame *frame,
|
||||
const GHOST_TWindowState state,
|
||||
const GHOST_TWindowState state_current)
|
||||
{
|
||||
const GHOST_TWindowState state_current = gwl_window_state_get(win);
|
||||
switch (state) {
|
||||
case GHOST_kWindowStateNormal:
|
||||
/* Unset states. */
|
||||
switch (state_current) {
|
||||
case GHOST_kWindowStateMaximized: {
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
libdecor_frame_unset_maximized(win->libdecor->frame);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xdg_toplevel_unset_maximized(win->xdg_decor->toplevel);
|
||||
}
|
||||
libdecor_frame_unset_maximized(frame);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateFullScreen: {
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
libdecor_frame_unset_fullscreen(win->libdecor->frame);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xdg_toplevel_unset_fullscreen(win->xdg_decor->toplevel);
|
||||
}
|
||||
libdecor_frame_unset_fullscreen(frame);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -207,46 +197,83 @@ static bool gwl_window_state_set(GWL_Window *win, const GHOST_TWindowState state
|
|||
}
|
||||
break;
|
||||
case GHOST_kWindowStateMaximized: {
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
libdecor_frame_set_maximized(win->libdecor->frame);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xdg_toplevel_set_maximized(win->xdg_decor->toplevel);
|
||||
}
|
||||
libdecor_frame_set_maximized(frame);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateMinimized: {
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
libdecor_frame_set_minimized(win->libdecor->frame);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xdg_toplevel_set_minimized(win->xdg_decor->toplevel);
|
||||
}
|
||||
libdecor_frame_set_minimized(frame);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateFullScreen: {
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
libdecor_frame_set_fullscreen(win->libdecor->frame, nullptr);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xdg_toplevel_set_fullscreen(win->xdg_decor->toplevel, nullptr);
|
||||
}
|
||||
libdecor_frame_set_fullscreen(frame, nullptr);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateEmbedded: {
|
||||
return GHOST_kFailure;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return GHOST_kSuccess;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
|
||||
|
||||
/**
|
||||
* \note Keep in sync with #gwl_window_state_set_for_libdecor.
|
||||
*/
|
||||
static bool gwl_window_state_set_for_xdg(struct xdg_toplevel *toplevel,
|
||||
const GHOST_TWindowState state,
|
||||
const GHOST_TWindowState state_current)
|
||||
{
|
||||
switch (state) {
|
||||
case GHOST_kWindowStateNormal:
|
||||
/* Unset states. */
|
||||
switch (state_current) {
|
||||
case GHOST_kWindowStateMaximized: {
|
||||
xdg_toplevel_unset_maximized(toplevel);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateFullScreen: {
|
||||
xdg_toplevel_unset_fullscreen(toplevel);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GHOST_kWindowStateMaximized: {
|
||||
xdg_toplevel_set_maximized(toplevel);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateMinimized: {
|
||||
xdg_toplevel_set_minimized(toplevel);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateFullScreen: {
|
||||
xdg_toplevel_set_fullscreen(toplevel, nullptr);
|
||||
break;
|
||||
}
|
||||
case GHOST_kWindowStateEmbedded: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gwl_window_state_set(GWL_Window *win, const GHOST_TWindowState state)
|
||||
{
|
||||
const GHOST_TWindowState state_current = gwl_window_state_get(win);
|
||||
bool result;
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
result = gwl_window_state_set_for_libdecor(win->libdecor->frame, state, state_current);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
result = gwl_window_state_set_for_xdg(win->xdg_decor->toplevel, state, state_current);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -326,6 +353,8 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
|
|||
|
||||
#endif
|
||||
|
||||
bool do_redraw = false;
|
||||
|
||||
if (win->frame_pending.size[0] != 0 && win->frame_pending.size[1] != 0) {
|
||||
if ((win->frame.size[0] != win->frame_pending.size[0]) ||
|
||||
(win->frame.size[1] != win->frame_pending.size[1])) {
|
||||
|
@ -333,6 +362,9 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
|
|||
}
|
||||
}
|
||||
|
||||
bool is_active_ghost = (win->ghost_window ==
|
||||
win->ghost_system->getWindowManager()->getActiveWindow());
|
||||
|
||||
if (win->frame_pending.is_active) {
|
||||
win->ghost_window->activate();
|
||||
}
|
||||
|
@ -340,6 +372,10 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
|
|||
win->ghost_window->deactivate();
|
||||
}
|
||||
|
||||
if (is_active_ghost != win->frame_pending.is_active) {
|
||||
do_redraw = true;
|
||||
}
|
||||
|
||||
win->frame_pending.size[0] = win->frame.size[0];
|
||||
win->frame_pending.size[1] = win->frame.size[1];
|
||||
|
||||
|
@ -348,6 +384,15 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
|
|||
/* Signal not to apply the scale unless it's configured. */
|
||||
win->frame_pending.size[0] = 0;
|
||||
win->frame_pending.size[1] = 0;
|
||||
|
||||
if (do_redraw) {
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
/* Could swap buffers, use pending to a redundant call in some cases. */
|
||||
gwl_window_pending_actions_tag(win, PENDING_SWAP_BUFFERS);
|
||||
#else
|
||||
win->ghost_window->swapBuffers();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void gwl_window_frame_update_from_pending(GWL_Window *win)
|
||||
|
@ -543,14 +588,13 @@ static void frame_handle_configure(struct libdecor_frame *frame,
|
|||
win->libdecor->configured = true;
|
||||
}
|
||||
|
||||
/* Apply the changes. */
|
||||
{
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
# ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
GHOST_SystemWayland *system = win->ghost_system;
|
||||
const bool is_main_thread = system->main_thread_id == std::this_thread::get_id();
|
||||
if (!is_main_thread) {
|
||||
/* NOTE(@campbellbarton): this only gets one redraw,
|
||||
* I could not find a case where this causes problems. */
|
||||
gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE);
|
||||
}
|
||||
else
|
||||
|
@ -747,11 +791,6 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
std::lock_guard lock_server_guard{*system->server_mutex};
|
||||
#endif
|
||||
|
||||
/* Globally store pointer to window manager. */
|
||||
if (!window_manager) {
|
||||
window_manager = system_->getWindowManager();
|
||||
}
|
||||
|
||||
window_->ghost_window = this;
|
||||
window_->ghost_system = system;
|
||||
|
||||
|
@ -795,7 +834,13 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
* simply called `blender.desktop`, so the it's important to follow that name.
|
||||
* Other distributions such as SNAP & FLATPAK may need to change this value T101779.
|
||||
* Currently there isn't a way to configure this, we may want to support that. */
|
||||
const char *xdg_app_id = "blender";
|
||||
const char *xdg_app_id = (
|
||||
#ifdef WITH_GHOST_WAYLAND_APP_ID
|
||||
STRINGIFY(WITH_GHOST_WAYLAND_APP_ID)
|
||||
#else
|
||||
"blender"
|
||||
#endif
|
||||
);
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
|
@ -854,26 +899,23 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
wl_surface_commit(window_->wl_surface);
|
||||
wl_display_roundtrip(system_->wl_display());
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
WGL_LibDecor_Window &decor = *window_->libdecor;
|
||||
/* It's important not to return until the window is configured or
|
||||
* calls to `setState` from Blender will crash `libdecor`. */
|
||||
while (!decor.configured) {
|
||||
if (libdecor_dispatch(system_->libdecor_context(), 0) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GHOST_OPENGL_ALPHA
|
||||
setOpaque();
|
||||
#endif
|
||||
|
||||
/* Causes a glitch with `libdecor` for some reason. */
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor == false)
|
||||
if (use_libdecor) {
|
||||
/* Additional round-trip is needed to ensure `xdg_toplevel` is set. */
|
||||
wl_display_roundtrip(system_->wl_display());
|
||||
|
||||
/* NOTE: LIBDECOR requires the window to be created & configured before the state can be set.
|
||||
* Workaround this by using the underlying `xdg_toplevel` */
|
||||
WGL_LibDecor_Window &decor = *window_->libdecor;
|
||||
struct xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(decor.frame);
|
||||
gwl_window_state_set_for_xdg(toplevel, state, GHOST_kWindowStateNormal);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
gwl_window_state_set(window_, state);
|
||||
|
@ -888,13 +930,13 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
setSwapInterval(0);
|
||||
}
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
GHOST_TSuccess GHOST_WindowWayland::swapBuffers()
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
GHOST_ASSERT(system_->main_thread_id == std::this_thread::get_id(), "Only from main thread!");
|
||||
#endif
|
||||
return GHOST_Window::swapBuffers();
|
||||
}
|
||||
#endif /* USE_EVENT_BACKGROUND_THREAD */
|
||||
|
||||
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
|
||||
{
|
||||
|
@ -924,6 +966,9 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mo
|
|||
|
||||
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*system_->server_mutex};
|
||||
#endif
|
||||
const GHOST_TSuccess ok = system_->cursor_shape_set(shape);
|
||||
m_cursorShape = (ok == GHOST_kSuccess) ? shape : GHOST_kStandardCursorDefault;
|
||||
return ok;
|
||||
|
@ -940,6 +985,9 @@ bool GHOST_WindowWayland::getCursorGrabUseSoftwareDisplay()
|
|||
GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape(
|
||||
uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*system_->server_mutex};
|
||||
#endif
|
||||
return system_->cursor_shape_custom_set(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor);
|
||||
}
|
||||
|
||||
|
@ -961,6 +1009,7 @@ void GHOST_WindowWayland::setTitle(const char *title)
|
|||
|
||||
std::string GHOST_WindowWayland::getTitle() const
|
||||
{
|
||||
/* No need to lock `server_mutex` (WAYLAND never changes this). */
|
||||
return window_->title.empty() ? "untitled" : window_->title;
|
||||
}
|
||||
|
||||
|
@ -971,6 +1020,7 @@ void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const
|
|||
|
||||
void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const
|
||||
{
|
||||
/* No need to lock `server_mutex` (WAYLAND never changes this in a thread). */
|
||||
bounds.set(0, 0, UNPACK2(window_->frame.size));
|
||||
}
|
||||
|
||||
|
@ -1053,6 +1103,9 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
|
|||
|
||||
uint16_t GHOST_WindowWayland::getDPIHint()
|
||||
{
|
||||
/* No need to lock `server_mutex`
|
||||
* (`outputs_changed_update_scale` never changes values in a non-main thread). */
|
||||
|
||||
/* Using the physical DPI will cause wrong scaling of the UI
|
||||
* use a multiplier for the default DPI as a workaround. */
|
||||
return wl_fixed_to_int(window_->scale_fractional * base_dpi);
|
||||
|
|
|
@ -23,13 +23,32 @@
|
|||
*
|
||||
* Solve this using a thread that handles events, locking must be performed as follows:
|
||||
*
|
||||
* - Lock #GWL_Display.server_mutex to prevent wl_display_dispatch / wl_display_roundtrip
|
||||
* - Lock #GWL_Display.server_mutex to prevent #wl_display_dispatch / #wl_display_roundtrip
|
||||
* running from multiple threads at once.
|
||||
* GHOST functions that communicate with WAYLAND must use this lock to to be thread safe.
|
||||
*
|
||||
* Even though WAYLAND functions that communicate with `wl_display_*` have their own locks,
|
||||
* GHOST functions that communicate with WAYLAND must use this lock to be thread safe.
|
||||
*
|
||||
* Without this reading/writing values such as changing the cursor or setting the window size
|
||||
* could conflict with WAYLAND callbacks running in a separate thread.
|
||||
* So the `server_mutex` ensures either GHOST or WAYLAND is manipulating this data,
|
||||
* having two WAYLAND callbacks accessing the data at once isn't a problem.
|
||||
*
|
||||
* \warning Some operations such as #ContextEGL creation/deletion & swap-buffers may call
|
||||
* #wl_display_dispatch indirectly, so it's important to take care to lock the `server_mutex`,
|
||||
* before accessing these functions too.
|
||||
*
|
||||
* \warning An unfortunate side-effect of this is care needs to be taken not to call public
|
||||
* GHOST functions from each other in situations that would otherwise be supported.
|
||||
* As both using a `server_mutex` results in a dead-lock. In some cases this means the
|
||||
* implementation and the public function may need to be split.
|
||||
*
|
||||
* - Lock #GWL_Display.timer_mutex when WAYLAND callbacks manipulate timers.
|
||||
*
|
||||
* - Lock #GWL_Display.events_pending_mutex before manipulating #GWL_Display.events_pending.
|
||||
*
|
||||
* - Lock #GWL_Window.frame_pending_mutex before changing window size & frame settings,
|
||||
* this is flushed in #GHOST_WindowWayland::pending_actions_handle.
|
||||
*/
|
||||
#define USE_EVENT_BACKGROUND_THREAD
|
||||
|
||||
|
@ -59,7 +78,9 @@ class GHOST_WindowWayland : public GHOST_Window {
|
|||
|
||||
/* Ghost API */
|
||||
|
||||
GHOST_TSuccess swapBuffers() override;
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
GHOST_TSuccess swapBuffers() override; /* Only for assertion. */
|
||||
#endif
|
||||
|
||||
uint16_t getDPIHint() override;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ WAYLAND_DYNLOAD_FN(libdecor_configuration_get_window_state)
|
|||
WAYLAND_DYNLOAD_FN(libdecor_decorate)
|
||||
WAYLAND_DYNLOAD_FN(libdecor_dispatch)
|
||||
WAYLAND_DYNLOAD_FN(libdecor_frame_commit)
|
||||
WAYLAND_DYNLOAD_FN(libdecor_frame_get_xdg_toplevel)
|
||||
WAYLAND_DYNLOAD_FN(libdecor_frame_map)
|
||||
WAYLAND_DYNLOAD_FN(libdecor_frame_set_app_id)
|
||||
WAYLAND_DYNLOAD_FN(libdecor_frame_set_fullscreen)
|
||||
|
@ -73,6 +74,7 @@ struct WaylandDynload_Libdecor {
|
|||
void WL_DYN_FN(libdecor_frame_commit)(struct libdecor_frame *frame,
|
||||
struct libdecor_state *state,
|
||||
struct libdecor_configuration *configuration);
|
||||
struct xdg_toplevel *WL_DYN_FN(libdecor_frame_get_xdg_toplevel)(struct libdecor_frame *frame);
|
||||
void WL_DYN_FN(libdecor_frame_map)(struct libdecor_frame *frame);
|
||||
void WL_DYN_FN(libdecor_frame_set_app_id)(struct libdecor_frame *frame, const char *app_id);
|
||||
void WL_DYN_FN(libdecor_frame_set_fullscreen)(struct libdecor_frame *frame,
|
||||
|
@ -108,6 +110,8 @@ struct WaylandDynload_Libdecor {
|
|||
# define libdecor_dispatch(...) (*wayland_dynload_libdecor.libdecor_dispatch)(__VA_ARGS__)
|
||||
# define libdecor_frame_commit(...) \
|
||||
(*wayland_dynload_libdecor.libdecor_frame_commit)(__VA_ARGS__)
|
||||
# define libdecor_frame_get_xdg_toplevel(...) \
|
||||
(*wayland_dynload_libdecor.libdecor_frame_get_xdg_toplevel)(__VA_ARGS__)
|
||||
# define libdecor_frame_map(...) (*wayland_dynload_libdecor.libdecor_frame_map)(__VA_ARGS__)
|
||||
# define libdecor_frame_set_app_id(...) \
|
||||
(*wayland_dynload_libdecor.libdecor_frame_set_app_id)(__VA_ARGS__)
|
||||
|
|
|
@ -549,6 +549,7 @@ CUSTOM_PY_UI_FILES = [
|
|||
os.path.join("scripts", "startup", "bl_operators"),
|
||||
os.path.join("scripts", "modules", "rna_prop_ui.py"),
|
||||
os.path.join("scripts", "modules", "rna_keymap_ui.py"),
|
||||
os.path.join("scripts", "modules", "bpy_types.py"),
|
||||
os.path.join("scripts", "presets", "keyconfig"),
|
||||
]
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ from bpy.props import (
|
|||
EnumProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
class ANIM_OT_keying_set_export(Operator):
|
||||
|
@ -111,7 +112,7 @@ class ANIM_OT_keying_set_export(Operator):
|
|||
break
|
||||
|
||||
if not found:
|
||||
self.report({'WARN'}, "Could not find material or light using Shader Node Tree - %s" % (ksp.id))
|
||||
self.report({'WARN'}, tip_("Could not find material or light using Shader Node Tree - %s") % (ksp.id))
|
||||
elif ksp.id.bl_rna.identifier.startswith("CompositorNodeTree"):
|
||||
# Find compositor nodetree using this node tree...
|
||||
for scene in bpy.data.scenes:
|
||||
|
@ -119,7 +120,7 @@ class ANIM_OT_keying_set_export(Operator):
|
|||
id_bpy_path = "bpy.data.scenes[\"%s\"].node_tree" % (scene.name)
|
||||
break
|
||||
else:
|
||||
self.report({'WARN'}, "Could not find scene using Compositor Node Tree - %s" % (ksp.id))
|
||||
self.report({'WARN'}, tip_("Could not find scene using Compositor Node Tree - %s") % (ksp.id))
|
||||
elif ksp.id.bl_rna.name == "Key":
|
||||
# "keys" conflicts with a Python keyword, hence the simple solution won't work
|
||||
id_bpy_path = "bpy.data.shape_keys[\"%s\"]" % (ksp.id.name)
|
||||
|
@ -324,7 +325,7 @@ class ClearUselessActions(Operator):
|
|||
action.user_clear()
|
||||
removed += 1
|
||||
|
||||
self.report({'INFO'}, "Removed %d empty and/or fake-user only Actions"
|
||||
self.report({'INFO'}, tip_("Removed %d empty and/or fake-user only Actions")
|
||||
% removed)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
@ -409,7 +410,7 @@ class UpdateAnimatedTransformConstraint(Operator):
|
|||
print(log)
|
||||
text = bpy.data.texts.new("UpdateAnimatedTransformConstraint Report")
|
||||
text.from_string(log)
|
||||
self.report({'INFO'}, "Complete report available on '%s' text datablock" % text.name)
|
||||
self.report({'INFO'}, tip_("Complete report available on '%s' text datablock") % text.name)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,10 @@ from __future__ import annotations
|
|||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
from bpy.app.translations import pgettext_data as data_
|
||||
from bpy.app.translations import (
|
||||
pgettext_data as data_,
|
||||
pgettext_tip as tip_,
|
||||
)
|
||||
|
||||
|
||||
from bpy_extras.asset_utils import (
|
||||
|
@ -125,7 +127,7 @@ class ASSET_OT_open_containing_blend_file(Operator):
|
|||
return {'RUNNING_MODAL'}
|
||||
|
||||
if returncode:
|
||||
self.report({'WARNING'}, "Blender sub-process exited with error code %d" % returncode)
|
||||
self.report({'WARNING'}, tip_("Blender sub-process exited with error code %d") % returncode)
|
||||
|
||||
if bpy.ops.asset.library_refresh.poll():
|
||||
bpy.ops.asset.library_refresh()
|
||||
|
|
|
@ -6,6 +6,7 @@ from mathutils import (
|
|||
Vector,
|
||||
Matrix,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
def CLIP_spaces_walk(context, all_screens, tarea, tspace, callback, *args):
|
||||
|
@ -193,7 +194,7 @@ class CLIP_OT_filter_tracks(Operator):
|
|||
|
||||
def execute(self, context):
|
||||
num_tracks = self._filter_values(context, self.track_threshold)
|
||||
self.report({'INFO'}, "Identified %d problematic tracks" % num_tracks)
|
||||
self.report({'INFO'}, tip_("Identified %d problematic tracks") % num_tracks)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ from bpy.props import (
|
|||
CollectionProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
# ########## Datablock previews... ##########
|
||||
|
||||
|
@ -123,7 +124,7 @@ class WM_OT_previews_batch_generate(Operator):
|
|||
if not self.use_backups:
|
||||
cmd.append("--no_backups")
|
||||
if subprocess.call(cmd):
|
||||
self.report({'ERROR'}, "Previews generation process failed for file '%s'!" % blen_path)
|
||||
self.report({'ERROR'}, tip_("Previews generation process failed for file '%s'!") % blen_path)
|
||||
context.window_manager.progress_end()
|
||||
return {'CANCELLED'}
|
||||
context.window_manager.progress_update(i + 1)
|
||||
|
@ -234,7 +235,7 @@ class WM_OT_previews_batch_clear(Operator):
|
|||
if not self.use_backups:
|
||||
cmd.append("--no_backups")
|
||||
if subprocess.call(cmd):
|
||||
self.report({'ERROR'}, "Previews clear process failed for file '%s'!" % blen_path)
|
||||
self.report({'ERROR'}, tip_("Previews clear process failed for file '%s'!") % blen_path)
|
||||
context.window_manager.progress_end()
|
||||
return {'CANCELLED'}
|
||||
context.window_manager.progress_update(i + 1)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import StringProperty
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
class EditExternally(Operator):
|
||||
|
@ -52,8 +53,8 @@ class EditExternally(Operator):
|
|||
|
||||
if not os.path.exists(filepath) or not os.path.isfile(filepath):
|
||||
self.report({'ERROR'},
|
||||
"Image path %r not found, image may be packed or "
|
||||
"unsaved" % filepath)
|
||||
tip_("Image path %r not found, image may be packed or "
|
||||
"unsaved") % filepath)
|
||||
return {'CANCELLED'}
|
||||
|
||||
cmd = self._editor_guess(context) + [filepath]
|
||||
|
@ -183,7 +184,7 @@ class ProjectApply(Operator):
|
|||
except KeyError:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self.report({'ERROR'}, "Could not find image '%s'" % image_name)
|
||||
self.report({'ERROR'}, tip_("Could not find image '%s'") % image_name)
|
||||
return {'CANCELLED'}
|
||||
|
||||
image.reload()
|
||||
|
|
|
@ -7,6 +7,7 @@ from bpy.props import (
|
|||
EnumProperty,
|
||||
IntProperty,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
class MeshMirrorUV(Operator):
|
||||
|
@ -164,18 +165,18 @@ class MeshMirrorUV(Operator):
|
|||
|
||||
if total_duplicates and total_no_active_UV:
|
||||
self.report({'WARNING'},
|
||||
"%d mesh(es) with no active UV layer, "
|
||||
"%d duplicates found in %d mesh(es), mirror may be incomplete"
|
||||
tip_("%d mesh(es) with no active UV layer, "
|
||||
"%d duplicates found in %d mesh(es), mirror may be incomplete")
|
||||
% (total_no_active_UV,
|
||||
total_duplicates,
|
||||
meshes_with_duplicates))
|
||||
elif total_no_active_UV:
|
||||
self.report({'WARNING'},
|
||||
"%d mesh(es) with no active UV layer"
|
||||
tip_("%d mesh(es) with no active UV layer")
|
||||
% (total_no_active_UV,))
|
||||
elif total_duplicates:
|
||||
self.report({'WARNING'},
|
||||
"%d duplicates found in %d mesh(es), mirror may be incomplete"
|
||||
tip_("%d duplicates found in %d mesh(es), mirror may be incomplete")
|
||||
% (total_duplicates, meshes_with_duplicates))
|
||||
|
||||
return {'FINISHED'}
|
||||
|
|
|
@ -8,6 +8,7 @@ from bpy.props import (
|
|||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
class SelectPattern(Operator):
|
||||
|
@ -363,14 +364,12 @@ class ShapeTransfer(Operator):
|
|||
for ob_other in objects:
|
||||
if ob_other.type != 'MESH':
|
||||
self.report({'WARNING'},
|
||||
("Skipping '%s', "
|
||||
"not a mesh") % ob_other.name)
|
||||
tip_("Skipping '%s', not a mesh") % ob_other.name)
|
||||
continue
|
||||
me_other = ob_other.data
|
||||
if len(me_other.vertices) != len(me.vertices):
|
||||
self.report({'WARNING'},
|
||||
("Skipping '%s', "
|
||||
"vertex count differs") % ob_other.name)
|
||||
tip_("Skipping '%s', vertex count differs") % ob_other.name)
|
||||
continue
|
||||
|
||||
target_normals = me_nos(me_other.vertices)
|
||||
|
@ -508,7 +507,7 @@ class JoinUVs(Operator):
|
|||
|
||||
if not mesh.uv_layers:
|
||||
self.report({'WARNING'},
|
||||
"Object: %s, Mesh: '%s' has no UVs"
|
||||
tip_("Object: %s, Mesh: '%s' has no UVs")
|
||||
% (obj.name, mesh.name))
|
||||
else:
|
||||
nbr_loops = len(mesh.loops)
|
||||
|
@ -531,9 +530,10 @@ class JoinUVs(Operator):
|
|||
mesh_other.tag = True
|
||||
|
||||
if len(mesh_other.loops) != nbr_loops:
|
||||
self.report({'WARNING'}, "Object: %s, Mesh: "
|
||||
"'%s' has %d loops (for %d faces),"
|
||||
" expected %d\n"
|
||||
self.report({'WARNING'},
|
||||
tip_("Object: %s, Mesh: "
|
||||
"'%s' has %d loops (for %d faces),"
|
||||
" expected %d\n")
|
||||
% (obj_other.name,
|
||||
mesh_other.name,
|
||||
len(mesh_other.loops),
|
||||
|
@ -547,9 +547,10 @@ class JoinUVs(Operator):
|
|||
mesh_other.uv_layers.new()
|
||||
uv_other = mesh_other.uv_layers.active
|
||||
if not uv_other:
|
||||
self.report({'ERROR'}, "Could not add "
|
||||
"a new UV map to object "
|
||||
"'%s' (Mesh '%s')\n"
|
||||
self.report({'ERROR'},
|
||||
tip_("Could not add "
|
||||
"a new UV map to object "
|
||||
"'%s' (Mesh '%s')\n")
|
||||
% (obj_other.name,
|
||||
mesh_other.name,
|
||||
),
|
||||
|
@ -784,8 +785,8 @@ class TransformsToDeltasAnim(Operator):
|
|||
adt = obj.animation_data
|
||||
if (adt is None) or (adt.action is None):
|
||||
self.report({'WARNING'},
|
||||
"No animation data to convert on object: %r" %
|
||||
obj.name)
|
||||
tip_("No animation data to convert on object: %r")
|
||||
% obj.name)
|
||||
continue
|
||||
|
||||
# first pass over F-Curves: ensure that we don't have conflicting
|
||||
|
@ -811,8 +812,8 @@ class TransformsToDeltasAnim(Operator):
|
|||
if fcu.array_index in existingFCurves[dpath]:
|
||||
# conflict
|
||||
self.report({'ERROR'},
|
||||
"Object '%r' already has '%r' F-Curve(s). "
|
||||
"Remove these before trying again" %
|
||||
tip_("Object '%r' already has '%r' F-Curve(s). "
|
||||
"Remove these before trying again") %
|
||||
(obj.name, dpath))
|
||||
return {'CANCELLED'}
|
||||
else:
|
||||
|
|
|
@ -9,6 +9,7 @@ from bpy.props import (
|
|||
FloatProperty,
|
||||
IntProperty,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
def object_ensure_material(obj, mat_name):
|
||||
|
@ -176,8 +177,8 @@ class QuickExplode(ObjectModeOperator, Operator):
|
|||
for obj in mesh_objects:
|
||||
if obj.particle_systems:
|
||||
self.report({'ERROR'},
|
||||
"Object %r already has a "
|
||||
"particle system" % obj.name)
|
||||
tip_("Object %r already has a "
|
||||
"particle system") % obj.name)
|
||||
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
|
|
@ -10,8 +10,11 @@ from bpy.props import (
|
|||
BoolProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.app.translations import (
|
||||
pgettext_tip as tip_,
|
||||
pgettext_data as data_,
|
||||
)
|
||||
|
||||
from bpy.app.translations import pgettext_data as data_
|
||||
|
||||
# For preset popover menu
|
||||
WindowManager.preset_name = StringProperty(
|
||||
|
@ -186,7 +189,7 @@ class AddPresetBase:
|
|||
else:
|
||||
os.remove(filepath)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, "Unable to remove preset: %r" % e)
|
||||
self.report({'ERROR'}, tip_("Unable to remove preset: %r") % e)
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
@ -236,7 +239,7 @@ class ExecutePreset(Operator):
|
|||
ext = splitext(filepath)[1].lower()
|
||||
|
||||
if ext not in {".py", ".xml"}:
|
||||
self.report({'ERROR'}, "Unknown file type: %r" % ext)
|
||||
self.report({'ERROR'}, tip_("Unknown file type: %r") % ext)
|
||||
return {'CANCELLED'}
|
||||
|
||||
if hasattr(preset_class, "reset_cb"):
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from bpy.props import (
|
|||
FloatProperty,
|
||||
IntProperty,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
class SequencerCrossfadeSounds(Operator):
|
||||
|
@ -231,7 +232,7 @@ class SequencerFadesAdd(Operator):
|
|||
sequence.invalidate_cache('COMPOSITE')
|
||||
|
||||
sequence_string = "sequence" if len(faded_sequences) == 1 else "sequences"
|
||||
self.report({'INFO'}, "Added fade animation to %d %s" % (len(faded_sequences), sequence_string))
|
||||
self.report({'INFO'}, tip_("Added fade animation to %d %s") % (len(faded_sequences), sequence_string))
|
||||
return {'FINISHED'}
|
||||
|
||||
def calculate_fade_duration(self, context, sequence):
|
||||
|
|
|
@ -12,8 +12,10 @@ from bpy.props import (
|
|||
StringProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
from bpy.app.translations import (
|
||||
pgettext_iface as iface_,
|
||||
pgettext_tip as tip_,
|
||||
)
|
||||
|
||||
|
||||
def _zipfile_root_namelist(file_to_extract):
|
||||
|
@ -224,7 +226,7 @@ class PREFERENCES_OT_keyconfig_import(Operator):
|
|||
else:
|
||||
shutil.move(self.filepath, path)
|
||||
except Exception as ex:
|
||||
self.report({'ERROR'}, "Installing keymap failed: %s" % ex)
|
||||
self.report({'ERROR'}, tip_("Installing keymap failed: %s") % ex)
|
||||
return {'CANCELLED'}
|
||||
|
||||
# sneaky way to check we're actually running the code.
|
||||
|
@ -450,11 +452,11 @@ class PREFERENCES_OT_addon_enable(Operator):
|
|||
if info_ver > bpy.app.version:
|
||||
self.report(
|
||||
{'WARNING'},
|
||||
"This script was written Blender "
|
||||
"version %d.%d.%d and might not "
|
||||
"function (correctly), "
|
||||
"though it is enabled" %
|
||||
info_ver
|
||||
tip_("This script was written Blender "
|
||||
"version %d.%d.%d and might not "
|
||||
"function (correctly), "
|
||||
"though it is enabled")
|
||||
% info_ver
|
||||
)
|
||||
return {'FINISHED'}
|
||||
else:
|
||||
|
@ -538,7 +540,7 @@ class PREFERENCES_OT_theme_install(Operator):
|
|||
|
||||
if not self.overwrite:
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report({'WARNING'}, tip_("File already installed to %r\n") % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try:
|
||||
|
@ -645,7 +647,7 @@ class PREFERENCES_OT_addon_install(Operator):
|
|||
pyfile_dir = os.path.dirname(pyfile)
|
||||
for addon_path in addon_utils.paths():
|
||||
if os.path.samefile(pyfile_dir, addon_path):
|
||||
self.report({'ERROR'}, "Source file is in the add-on search path: %r" % addon_path)
|
||||
self.report({'ERROR'}, tip_("Source file is in the add-on search path: %r") % addon_path)
|
||||
return {'CANCELLED'}
|
||||
del addon_path
|
||||
del pyfile_dir
|
||||
|
@ -669,7 +671,7 @@ class PREFERENCES_OT_addon_install(Operator):
|
|||
for f in file_to_extract_root:
|
||||
path_dest = os.path.join(path_addons, os.path.basename(f))
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report({'WARNING'}, tip_("File already installed to %r\n") % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try: # extract the file to "addons"
|
||||
|
@ -684,7 +686,7 @@ class PREFERENCES_OT_addon_install(Operator):
|
|||
if self.overwrite:
|
||||
_module_filesystem_remove(path_addons, os.path.basename(pyfile))
|
||||
elif os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report({'WARNING'}, tip_("File already installed to %r\n") % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
# if not compressed file just copy into the addon path
|
||||
|
@ -764,7 +766,7 @@ class PREFERENCES_OT_addon_remove(Operator):
|
|||
|
||||
path, isdir = PREFERENCES_OT_addon_remove.path_from_addon(self.module)
|
||||
if path is None:
|
||||
self.report({'WARNING'}, "Add-on path %r could not be found" % path)
|
||||
self.report({'WARNING'}, tip_("Add-on path %r could not be found") % path)
|
||||
return {'CANCELLED'}
|
||||
|
||||
# in case its enabled
|
||||
|
@ -783,9 +785,9 @@ class PREFERENCES_OT_addon_remove(Operator):
|
|||
|
||||
# lame confirmation check
|
||||
def draw(self, _context):
|
||||
self.layout.label(text="Remove Add-on: %r?" % self.module)
|
||||
self.layout.label(text=iface_("Remove Add-on: %r?") % self.module, translate=False)
|
||||
path, _isdir = PREFERENCES_OT_addon_remove.path_from_addon(self.module)
|
||||
self.layout.label(text="Path: %r" % path)
|
||||
self.layout.label(text=iface_("Path: %r") % path, translate=False)
|
||||
|
||||
def invoke(self, context, _event):
|
||||
wm = context.window_manager
|
||||
|
@ -914,7 +916,7 @@ class PREFERENCES_OT_app_template_install(Operator):
|
|||
for f in file_to_extract_root:
|
||||
path_dest = os.path.join(path_app_templates, os.path.basename(f))
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report({'WARNING'}, tip_("File already installed to %r\n") % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try: # extract the file to "bl_app_templates_user"
|
||||
|
@ -925,7 +927,7 @@ class PREFERENCES_OT_app_template_install(Operator):
|
|||
|
||||
else:
|
||||
# Only support installing zipfiles
|
||||
self.report({'WARNING'}, "Expected a zip-file %r\n" % filepath)
|
||||
self.report({'WARNING'}, tip_("Expected a zip-file %r\n") % filepath)
|
||||
return {'CANCELLED'}
|
||||
|
||||
app_templates_new = set(os.listdir(path_app_templates)) - app_templates_old
|
||||
|
|
|
@ -772,7 +772,7 @@ class WM_OT_operator_pie_enum(Operator):
|
|||
try:
|
||||
op_rna = op.get_rna_type()
|
||||
except KeyError:
|
||||
self.report({'ERROR'}, "Operator not found: bpy.ops.%s" % data_path)
|
||||
self.report({'ERROR'}, tip_("Operator not found: bpy.ops.%s") % data_path)
|
||||
return {'CANCELLED'}
|
||||
|
||||
def draw_cb(self, context):
|
||||
|
@ -872,7 +872,7 @@ class WM_OT_context_collection_boolean_set(Operator):
|
|||
elif value_orig is False:
|
||||
pass
|
||||
else:
|
||||
self.report({'WARNING'}, "Non boolean value found: %s[ ].%s" %
|
||||
self.report({'WARNING'}, tip_("Non boolean value found: %s[ ].%s") %
|
||||
(data_path_iter, data_path_item))
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
@ -975,7 +975,7 @@ class WM_OT_context_modal_mouse(Operator):
|
|||
(item, ) = self._values.keys()
|
||||
header_text = header_text % eval("item.%s" % self.data_path_item)
|
||||
else:
|
||||
header_text = (self.header_text % delta) + " (delta)"
|
||||
header_text = (self.header_text % delta) + tip_(" (delta)")
|
||||
context.area.header_text_set(header_text)
|
||||
|
||||
elif 'LEFTMOUSE' == event_type:
|
||||
|
@ -995,7 +995,7 @@ class WM_OT_context_modal_mouse(Operator):
|
|||
self._values_store(context)
|
||||
|
||||
if not self._values:
|
||||
self.report({'WARNING'}, "Nothing to operate on: %s[ ].%s" %
|
||||
self.report({'WARNING'}, tip_("Nothing to operate on: %s[ ].%s") %
|
||||
(self.data_path_iter, self.data_path_item))
|
||||
|
||||
return {'CANCELLED'}
|
||||
|
@ -1133,7 +1133,7 @@ class WM_OT_path_open(Operator):
|
|||
filepath = os.path.normpath(filepath)
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
self.report({'ERROR'}, "File '%s' not found" % filepath)
|
||||
self.report({'ERROR'}, tip_("File '%s' not found") % filepath)
|
||||
return {'CANCELLED'}
|
||||
|
||||
if sys.platform[:3] == "win":
|
||||
|
@ -1204,7 +1204,7 @@ def _wm_doc_get_id(doc_id, *, do_url=True, url_prefix="", report=None):
|
|||
|
||||
if rna_class is None:
|
||||
if report is not None:
|
||||
report({'ERROR'}, iface_("Type \"%s\" can not be found") % class_name)
|
||||
report({'ERROR'}, tip_("Type \"%s\" can not be found") % class_name)
|
||||
return None
|
||||
|
||||
# Detect if this is a inherited member and use that name instead.
|
||||
|
@ -1275,9 +1275,9 @@ class WM_OT_doc_view_manual(Operator):
|
|||
if url is None:
|
||||
self.report(
|
||||
{'WARNING'},
|
||||
"No reference available %r, "
|
||||
"Update info in 'rna_manual_reference.py' "
|
||||
"or callback to bpy.utils.manual_map()" %
|
||||
tip_("No reference available %r, "
|
||||
"Update info in 'rna_manual_reference.py' "
|
||||
"or callback to bpy.utils.manual_map()") %
|
||||
self.doc_id
|
||||
)
|
||||
return {'CANCELLED'}
|
||||
|
@ -2156,7 +2156,7 @@ class WM_OT_tool_set_by_id(Operator):
|
|||
tool_settings.workspace_tool_type = 'FALLBACK'
|
||||
return {'FINISHED'}
|
||||
else:
|
||||
self.report({'WARNING'}, "Tool %r not found for space %r" % (self.name, space_type))
|
||||
self.report({'WARNING'}, tip_("Tool %r not found for space %r") % (self.name, space_type))
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
|
@ -2289,7 +2289,7 @@ class WM_OT_toolbar_fallback_pie(Operator):
|
|||
ToolSelectPanelHelper.draw_fallback_tool_items_for_pie_menu(self.layout, context)
|
||||
|
||||
wm = context.window_manager
|
||||
wm.popup_menu_pie(draw_func=draw_cb, title="Fallback Tool", event=event)
|
||||
wm.popup_menu_pie(draw_func=draw_cb, title=iface_("Fallback Tool"), event=event)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
@ -2943,7 +2943,7 @@ class WM_OT_batch_rename(Operator):
|
|||
row.prop(action, "op_remove", text="", icon='REMOVE')
|
||||
row.prop(action, "op_add", text="", icon='ADD')
|
||||
|
||||
layout.label(text=iface_("Rename %d %s") % (len(self._data[0]), self._data[2]))
|
||||
layout.label(text=iface_("Rename %d %s") % (len(self._data[0]), self._data[2]), translate=False)
|
||||
|
||||
def check(self, context):
|
||||
changed = False
|
||||
|
@ -3083,7 +3083,7 @@ class WM_MT_splash_quick_setup(Menu):
|
|||
|
||||
old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version()
|
||||
if bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version:
|
||||
sub.operator("preferences.copy_prev", text=iface_("Load %d.%d Settings", "Operator") % old_version)
|
||||
sub.operator("preferences.copy_prev", text=iface_("Load %d.%d Settings", "Operator") % old_version, translate=False)
|
||||
sub.operator("wm.save_userpref", text="Save New Settings")
|
||||
else:
|
||||
sub.label()
|
||||
|
|
|
@ -3,8 +3,10 @@ import bpy
|
|||
from bpy.types import Menu, Panel, UIList
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
from bpy.app.translations import (
|
||||
pgettext_tip as iface_,
|
||||
pgettext_tip as tip_,
|
||||
)
|
||||
|
||||
class MESH_MT_vertex_group_context_menu(Menu):
|
||||
bl_label = "Vertex Group Specials"
|
||||
|
@ -550,7 +552,8 @@ class MESH_UL_attributes(UIList):
|
|||
sub = split.row()
|
||||
sub.alignment = 'RIGHT'
|
||||
sub.active = False
|
||||
sub.label(text="%s ▶ %s" % (domain_name, data_type.name))
|
||||
sub.label(text="%s ▶ %s" % (iface_(domain_name), iface_(data_type.name)),
|
||||
translate=False)
|
||||
|
||||
|
||||
class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
|
||||
|
@ -651,7 +654,8 @@ class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
|
|||
sub = split.row()
|
||||
sub.alignment = 'RIGHT'
|
||||
sub.active = False
|
||||
sub.label(text="%s ▶ %s" % (domain_name, data_type.name))
|
||||
sub.label(text="%s ▶ %s" % (iface_(domain_name), iface_(data_type.name)),
|
||||
translate=False)
|
||||
|
||||
active_render = _index == data.color_attributes.render_color_index
|
||||
|
||||
|
|
|
@ -400,9 +400,12 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
|
|||
label = "ERROR"
|
||||
icon = 'ERROR'
|
||||
box.label(text=label, icon=icon)
|
||||
box.label(text="Iterations: %d .. %d (avg. %d)" %
|
||||
(result.min_iterations, result.max_iterations, result.avg_iterations))
|
||||
box.label(text="Error: %.5f .. %.5f (avg. %.5f)" % (result.min_error, result.max_error, result.avg_error))
|
||||
box.label(text=iface_("Iterations: %d .. %d (avg. %d)") %
|
||||
(result.min_iterations, result.max_iterations, result.avg_iterations),
|
||||
translate=False)
|
||||
box.label(text=iface_("Error: %.5f .. %.5f (avg. %.5f)")
|
||||
% (result.min_error, result.max_error, result.avg_error),
|
||||
translate=False)
|
||||
|
||||
|
||||
class PARTICLE_PT_hair_dynamics_collision(ParticleButtonsPanel, Panel):
|
||||
|
@ -756,7 +759,7 @@ class PARTICLE_PT_physics_fluid_advanced(ParticleButtonsPanel, Panel):
|
|||
particle_volume = part.mass / fluid.rest_density
|
||||
spacing = pow(particle_volume, 1.0 / 3.0)
|
||||
|
||||
sub.label(text="Spacing: %g" % spacing)
|
||||
sub.label(text=iface_("Spacing: %g") % spacing, translate=False)
|
||||
|
||||
|
||||
class PARTICLE_PT_physics_fluid_springs(ParticleButtonsPanel, Panel):
|
||||
|
|
|
@ -3,6 +3,7 @@ import bpy
|
|||
from bpy.types import (
|
||||
Panel,
|
||||
)
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
|
@ -67,7 +68,8 @@ class WORKSPACE_PT_addons(WorkSpaceButtonsPanel, Panel):
|
|||
row.operator(
|
||||
"wm.owner_disable" if is_enabled else "wm.owner_enable",
|
||||
icon='CHECKBOX_HLT' if is_enabled else 'CHECKBOX_DEHLT',
|
||||
text="%s: %s" % (info["category"], info["name"]),
|
||||
text=iface_("%s: %s" ) % (iface_(info["category"]), iface_(info["name"])),
|
||||
translate=False,
|
||||
emboss=False,
|
||||
).owner_id = module_name
|
||||
if is_enabled:
|
||||
|
|
|
@ -168,8 +168,9 @@ class CLIP_HT_header(Header):
|
|||
r = active_object.reconstruction
|
||||
|
||||
if r.is_valid and sc.view == 'CLIP':
|
||||
layout.label(text="Solve error: %.2f px" %
|
||||
(r.average_error))
|
||||
layout.label(text=iface_("Solve error: %.2f px") %
|
||||
(r.average_error),
|
||||
translate=False)
|
||||
|
||||
row = layout.row()
|
||||
row.prop(sc, "pivot_point", text="", icon_only=True)
|
||||
|
@ -744,8 +745,8 @@ class CLIP_PT_track(CLIP_PT_tracking_panel, Panel):
|
|||
layout.prop(act_track, "weight_stab")
|
||||
|
||||
if act_track.has_bundle:
|
||||
label_text = "Average Error: %.2f px" % (act_track.average_error)
|
||||
layout.label(text=label_text)
|
||||
label_text = iface_("Average Error: %.2f px") % (act_track.average_error)
|
||||
layout.label(text=label_text, translate=False)
|
||||
|
||||
layout.use_property_split = False
|
||||
|
||||
|
|
|
@ -407,15 +407,20 @@ class TOPBAR_MT_file_defaults(Menu):
|
|||
app_template = None
|
||||
|
||||
if app_template:
|
||||
layout.label(text=bpy.path.display_name(
|
||||
app_template, has_ext=False))
|
||||
layout.label(
|
||||
text=iface_(bpy.path.display_name(app_template, has_ext=False),
|
||||
i18n_contexts.id_workspace), translate=False)
|
||||
|
||||
layout.operator("wm.save_homefile")
|
||||
if app_template:
|
||||
display_name = bpy.path.display_name(iface_(app_template))
|
||||
props = layout.operator("wm.read_factory_settings", text="Load Factory Blender Settings")
|
||||
props = layout.operator("wm.read_factory_settings",
|
||||
text="Load Factory Blender Settings")
|
||||
props.app_template = app_template
|
||||
props = layout.operator("wm.read_factory_settings", text="Load Factory %s Settings" % display_name)
|
||||
props = layout.operator("wm.read_factory_settings",
|
||||
text=iface_("Load Factory %s Settings",
|
||||
i18n_contexts.operator_default) % display_name,
|
||||
translate=False)
|
||||
props.app_template = app_template
|
||||
props.use_factory_startup_app_template_only = True
|
||||
del display_name
|
||||
|
|
|
@ -114,7 +114,9 @@ class USERPREF_MT_save_load(Menu):
|
|||
if app_template:
|
||||
display_name = bpy.path.display_name(iface_(app_template))
|
||||
layout.operator("wm.read_factory_userpref", text="Load Factory Blender Preferences")
|
||||
props = layout.operator("wm.read_factory_userpref", text="Load Factory %s Preferences" % display_name)
|
||||
props = layout.operator("wm.read_factory_userpref",
|
||||
text=iface_("Load Factory %s Preferences") % display_name,
|
||||
translate=False)
|
||||
props.use_factory_startup_app_template_only = True
|
||||
del display_name
|
||||
else:
|
||||
|
@ -2317,7 +2319,6 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
|
|||
({"property": "use_sculpt_texture_paint"}, "T96225"),
|
||||
({"property": "use_full_frame_compositor"}, "T88150"),
|
||||
({"property": "enable_eevee_next"}, "T93220"),
|
||||
({"property": "use_draw_manager_acquire_lock"}, "T98016"),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -18,7 +18,11 @@ from bl_ui.properties_grease_pencil_common import (
|
|||
from bl_ui.space_toolsystem_common import (
|
||||
ToolActivePanelHelper,
|
||||
)
|
||||
from bpy.app.translations import contexts as i18n_contexts
|
||||
from bpy.app.translations import (
|
||||
pgettext_iface as iface_,
|
||||
pgettext_tip as tip_,
|
||||
contexts as i18n_contexts,
|
||||
)
|
||||
|
||||
|
||||
class VIEW3D_HT_tool_header(Header):
|
||||
|
@ -700,7 +704,7 @@ class VIEW3D_HT_header(Header):
|
|||
sub.ui_units_x = 5.5
|
||||
sub.operator_menu_enum(
|
||||
"object.mode_set", "mode",
|
||||
text=bpy.app.translations.pgettext_iface(act_mode_item.name, act_mode_i18n_context),
|
||||
text=iface_(act_mode_item.name, act_mode_i18n_context),
|
||||
icon=act_mode_item.icon,
|
||||
)
|
||||
del act_mode_item
|
||||
|
@ -1137,7 +1141,9 @@ class VIEW3D_MT_mirror(Menu):
|
|||
|
||||
for (space_name, space_id) in (("Global", 'GLOBAL'), ("Local", 'LOCAL')):
|
||||
for axis_index, axis_name in enumerate("XYZ"):
|
||||
props = layout.operator("transform.mirror", text="%s %s" % (axis_name, space_name))
|
||||
props = layout.operator("transform.mirror",
|
||||
text="%s %s" % (axis_name, iface_(space_name)),
|
||||
translate=False)
|
||||
props.constraint_axis[axis_index] = True
|
||||
props.orient_type = space_id
|
||||
|
||||
|
@ -2553,16 +2559,16 @@ class VIEW3D_MT_object_context_menu(Menu):
|
|||
props.data_path_item = "data.lens"
|
||||
props.input_scale = 0.1
|
||||
if obj.data.lens_unit == 'MILLIMETERS':
|
||||
props.header_text = "Camera Focal Length: %.1fmm"
|
||||
props.header_text = tip_("Camera Focal Length: %.1fmm")
|
||||
else:
|
||||
props.header_text = "Camera Focal Length: %.1f\u00B0"
|
||||
props.header_text = tip_("Camera Focal Length: %.1f\u00B0")
|
||||
|
||||
else:
|
||||
props = layout.operator("wm.context_modal_mouse", text="Camera Lens Scale")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.ortho_scale"
|
||||
props.input_scale = 0.01
|
||||
props.header_text = "Camera Lens Scale: %.3f"
|
||||
props.header_text = tip_("Camera Lens Scale: %.3f")
|
||||
|
||||
if not obj.data.dof.focus_object:
|
||||
if view and view.camera == obj and view.region_3d.view_perspective == 'CAMERA':
|
||||
|
@ -2572,7 +2578,7 @@ class VIEW3D_MT_object_context_menu(Menu):
|
|||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.dof.focus_distance"
|
||||
props.input_scale = 0.02
|
||||
props.header_text = "Focus Distance: %.3f"
|
||||
props.header_text = tip_("Focus Distance: %.3f")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -2583,13 +2589,13 @@ class VIEW3D_MT_object_context_menu(Menu):
|
|||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.extrude"
|
||||
props.input_scale = 0.01
|
||||
props.header_text = "Extrude: %.3f"
|
||||
props.header_text = tip_("Extrude: %.3f")
|
||||
|
||||
props = layout.operator("wm.context_modal_mouse", text="Adjust Offset")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.offset"
|
||||
props.input_scale = 0.01
|
||||
props.header_text = "Offset: %.3f"
|
||||
props.header_text = tip_("Offset: %.3f")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -2600,7 +2606,7 @@ class VIEW3D_MT_object_context_menu(Menu):
|
|||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "empty_display_size"
|
||||
props.input_scale = 0.01
|
||||
props.header_text = "Empty Display Size: %.3f"
|
||||
props.header_text = tip_("Empty Display Size: %.3f")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -2618,36 +2624,36 @@ class VIEW3D_MT_object_context_menu(Menu):
|
|||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.energy"
|
||||
props.input_scale = 1.0
|
||||
props.header_text = "Light Power: %.3f"
|
||||
props.header_text = tip_("Light Power: %.3f")
|
||||
|
||||
if light.type == 'AREA':
|
||||
if light.shape in {'RECTANGLE', 'ELLIPSE'}:
|
||||
props = layout.operator("wm.context_modal_mouse", text="Adjust Area Light X Size")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.size"
|
||||
props.header_text = "Light Size X: %.3f"
|
||||
props.header_text = tip_("Light Size X: %.3f")
|
||||
|
||||
props = layout.operator("wm.context_modal_mouse", text="Adjust Area Light Y Size")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.size_y"
|
||||
props.header_text = "Light Size Y: %.3f"
|
||||
props.header_text = tip_("Light Size Y: %.3f")
|
||||
else:
|
||||
props = layout.operator("wm.context_modal_mouse", text="Adjust Area Light Size")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.size"
|
||||
props.header_text = "Light Size: %.3f"
|
||||
props.header_text = tip_("Light Size: %.3f")
|
||||
|
||||
elif light.type in {'SPOT', 'POINT'}:
|
||||
props = layout.operator("wm.context_modal_mouse", text="Adjust Light Radius")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.shadow_soft_size"
|
||||
props.header_text = "Light Radius: %.3f"
|
||||
props.header_text = tip_("Light Radius: %.3f")
|
||||
|
||||
elif light.type == 'SUN':
|
||||
props = layout.operator("wm.context_modal_mouse", text="Adjust Sun Light Angle")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.angle"
|
||||
props.header_text = "Light Angle: %.3f"
|
||||
props.header_text = tip_("Light Angle: %.3f")
|
||||
|
||||
if light.type == 'SPOT':
|
||||
layout.separator()
|
||||
|
@ -2656,13 +2662,13 @@ class VIEW3D_MT_object_context_menu(Menu):
|
|||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.spot_size"
|
||||
props.input_scale = 0.01
|
||||
props.header_text = "Spot Size: %.2f"
|
||||
props.header_text = tip_("Spot Size: %.2f")
|
||||
|
||||
props = layout.operator("wm.context_modal_mouse", text="Adjust Spot Light Blend")
|
||||
props.data_path_iter = "selected_editable_objects"
|
||||
props.data_path_item = "data.spot_blend"
|
||||
props.input_scale = -0.01
|
||||
props.header_text = "Spot Blend: %.2f"
|
||||
props.header_text = tip_("Spot Blend: %.2f")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -7108,13 +7114,13 @@ class VIEW3D_PT_overlay_gpencil_options(Panel):
|
|||
def draw_header(self, context):
|
||||
layout = self.layout
|
||||
layout.label(text={
|
||||
'PAINT_GPENCIL': "Draw Grease Pencil",
|
||||
'EDIT_GPENCIL': "Edit Grease Pencil",
|
||||
'SCULPT_GPENCIL': "Sculpt Grease Pencil",
|
||||
'WEIGHT_GPENCIL': "Weight Grease Pencil",
|
||||
'VERTEX_GPENCIL': "Vertex Grease Pencil",
|
||||
'OBJECT': "Grease Pencil",
|
||||
}[context.mode])
|
||||
'PAINT_GPENCIL': iface_("Draw Grease Pencil"),
|
||||
'EDIT_GPENCIL': iface_("Edit Grease Pencil"),
|
||||
'SCULPT_GPENCIL': iface_("Sculpt Grease Pencil"),
|
||||
'WEIGHT_GPENCIL': iface_("Weight Grease Pencil"),
|
||||
'VERTEX_GPENCIL': iface_("Vertex Grease Pencil"),
|
||||
'OBJECT': iface_("Grease Pencil"),
|
||||
}[context.mode], translate=False)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "DNA_asset_types.h"
|
||||
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
|
@ -90,7 +91,7 @@ struct AssetLibrary {
|
|||
* already in memory and which not. Neither do we keep track of how many parts of Blender are
|
||||
* using an asset or an asset library, which is needed to know when assets can be freed.
|
||||
*/
|
||||
Vector<std::unique_ptr<AssetRepresentation>> asset_storage_;
|
||||
Set<std::unique_ptr<AssetRepresentation>> asset_storage_;
|
||||
|
||||
std::optional<int> find_asset_index(const AssetRepresentation &asset);
|
||||
};
|
||||
|
|
|
@ -139,39 +139,25 @@ void AssetLibrary::refresh()
|
|||
AssetRepresentation &AssetLibrary::add_external_asset(StringRef name,
|
||||
std::unique_ptr<AssetMetaData> metadata)
|
||||
{
|
||||
asset_storage_.append(std::make_unique<AssetRepresentation>(name, std::move(metadata)));
|
||||
return *asset_storage_.last();
|
||||
return *asset_storage_.lookup_key_or_add(
|
||||
std::make_unique<AssetRepresentation>(name, std::move(metadata)));
|
||||
}
|
||||
|
||||
AssetRepresentation &AssetLibrary::add_local_id_asset(ID &id)
|
||||
{
|
||||
asset_storage_.append(std::make_unique<AssetRepresentation>(id));
|
||||
return *asset_storage_.last();
|
||||
}
|
||||
|
||||
std::optional<int> AssetLibrary::find_asset_index(const AssetRepresentation &asset)
|
||||
{
|
||||
int index = 0;
|
||||
/* Find index of asset. */
|
||||
for (auto &asset_uptr : asset_storage_) {
|
||||
if (&asset == asset_uptr.get()) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return {};
|
||||
return *asset_storage_.lookup_key_or_add(std::make_unique<AssetRepresentation>(id));
|
||||
}
|
||||
|
||||
bool AssetLibrary::remove_asset(AssetRepresentation &asset)
|
||||
{
|
||||
std::optional<int> asset_index = find_asset_index(asset);
|
||||
if (!asset_index) {
|
||||
return false;
|
||||
}
|
||||
/* Create a "fake" unique_ptr to figure out the hash for the pointed to asset representation. The
|
||||
* standard requires that this is the same for all unique_ptr's wrapping the same address. */
|
||||
std::unique_ptr<AssetRepresentation> fake_asset_ptr{&asset};
|
||||
|
||||
asset_storage_.remove_and_reorder(*asset_index);
|
||||
return true;
|
||||
const bool was_removed = asset_storage_.remove_as(fake_asset_ptr);
|
||||
/* Make sure the contained storage is not destructed. */
|
||||
fake_asset_ptr.release();
|
||||
return was_removed;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -25,7 +25,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 0
|
||||
#define BLENDER_FILE_SUBVERSION 1
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
|
|
@ -90,6 +90,13 @@ typedef struct BPathForeachPathData {
|
|||
/** The root to use as base for relative paths. Only set if `BKE_BPATH_FOREACH_PATH_ABSOLUTE`
|
||||
* flag is set, NULL otherwise. */
|
||||
const char *absolute_base_path;
|
||||
|
||||
/** ID owning the path being processed. */
|
||||
struct ID *owner_id;
|
||||
|
||||
/** IDTypeInfo callbacks are responsible to set this boolean if they modified one or more paths.
|
||||
*/
|
||||
bool is_path_modified;
|
||||
} BPathForeachPathData;
|
||||
|
||||
/** Run `bpath_data.callback_function` on all paths contained in `id`. */
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
|
||||
#include <mutex>
|
||||
|
||||
#include "BLI_bounds_types.hh"
|
||||
#include "BLI_cache_mutex.hh"
|
||||
#include "BLI_float3x3.hh"
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_shared_cache.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
@ -63,9 +65,7 @@ struct BasisCache {
|
|||
} // namespace curves::nurbs
|
||||
|
||||
/**
|
||||
* Contains derived data, caches, and other information not saved in files, besides a few pointers
|
||||
* to arrays that are kept in the non-runtime struct to avoid dereferencing this whenever they are
|
||||
* accessed.
|
||||
* Contains derived data, caches, and other information not saved in files.
|
||||
*/
|
||||
class CurvesGeometryRuntime {
|
||||
public:
|
||||
|
@ -95,6 +95,13 @@ class CurvesGeometryRuntime {
|
|||
*/
|
||||
mutable Span<float3> evaluated_positions_span;
|
||||
|
||||
/**
|
||||
* A cache of bounds shared between data-blocks with unchanged positions and radii.
|
||||
* When data changes affect the bounds, the cache is "un-shared" with other geometries.
|
||||
* See #SharedCache comments.
|
||||
*/
|
||||
mutable SharedCache<Bounds<float3>> bounds_cache;
|
||||
|
||||
/**
|
||||
* Cache of lengths along each evaluated curve for each evaluated point. If a curve is
|
||||
* cyclic, it needs one more length value to correspond to the last segment, so in order to
|
||||
|
@ -391,6 +398,11 @@ class CurvesGeometry : public ::CurvesGeometry {
|
|||
void tag_topology_changed();
|
||||
/** Call after changing the "tilt" or "up" attributes. */
|
||||
void tag_normals_changed();
|
||||
/**
|
||||
* Call when making manual changes to the "radius" attribute. The attribute API will also call
|
||||
* this in #finish() calls.
|
||||
*/
|
||||
void tag_radii_changed();
|
||||
|
||||
void translate(const float3 &translation);
|
||||
void transform(const float4x4 &matrix);
|
||||
|
|
|
@ -381,15 +381,9 @@ const float (*BKE_mesh_poly_normals_ensure(const struct Mesh *mesh))[3];
|
|||
void BKE_mesh_normals_tag_dirty(struct Mesh *mesh);
|
||||
|
||||
/**
|
||||
* Check that a mesh with non-dirty normals has vertex and face custom data layers.
|
||||
* If these asserts fail, it means some area cleared the dirty flag but didn't copy or add the
|
||||
* normal layers, or removed normals but didn't set the dirty flag.
|
||||
*/
|
||||
void BKE_mesh_assert_normals_dirty_or_calculated(const struct Mesh *mesh);
|
||||
|
||||
/**
|
||||
* Retrieve write access to the vertex normal layer, ensuring that it exists and that it is not
|
||||
* shared. The provided vertex normals should be the same as if they were calculated automatically.
|
||||
* Retrieve write access to the cached vertex normals, ensuring that they are allocated but *not*
|
||||
* that they are calculated. The provided vertex normals should be the same as if they were
|
||||
* calculated automatically.
|
||||
*
|
||||
* \note In order to clear the dirty flag, this function should be followed by a call to
|
||||
* #BKE_mesh_vertex_normals_clear_dirty. This is separate so that normals are still tagged dirty
|
||||
|
@ -401,8 +395,9 @@ void BKE_mesh_assert_normals_dirty_or_calculated(const struct Mesh *mesh);
|
|||
float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3];
|
||||
|
||||
/**
|
||||
* Retrieve write access to the poly normal layer, ensuring that it exists and that it is not
|
||||
* shared. The provided poly normals should be the same as if they were calculated automatically.
|
||||
* Retrieve write access to the cached polygon normals, ensuring that they are allocated but *not*
|
||||
* that they are calculated. The provided polygon normals should be the same as if they were
|
||||
* calculated automatically.
|
||||
*
|
||||
* \note In order to clear the dirty flag, this function should be followed by a call to
|
||||
* #BKE_mesh_poly_normals_clear_dirty. This is separate so that normals are still tagged dirty
|
||||
|
@ -413,17 +408,6 @@ float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3];
|
|||
*/
|
||||
float (*BKE_mesh_poly_normals_for_write(struct Mesh *mesh))[3];
|
||||
|
||||
/**
|
||||
* Free any cached vertex or poly normals. Face corner (loop) normals are also derived data,
|
||||
* but are not handled with the same method yet, so they are not included. It's important that this
|
||||
* is called after the mesh changes size, since otherwise cached normal arrays might not be large
|
||||
* enough (though it may be called indirectly by other functions).
|
||||
*
|
||||
* \note Normally it's preferred to call #BKE_mesh_normals_tag_dirty instead,
|
||||
* but this can be used in specific situations to reset a mesh or reduce memory usage.
|
||||
*/
|
||||
void BKE_mesh_clear_derived_normals(struct Mesh *mesh);
|
||||
|
||||
/**
|
||||
* Mark the mesh's vertex normals non-dirty, for when they are calculated or assigned manually.
|
||||
*/
|
||||
|
@ -488,12 +472,6 @@ void BKE_mesh_calc_normals(struct Mesh *me);
|
|||
* Called after calculating all modifiers.
|
||||
*/
|
||||
void BKE_mesh_ensure_normals_for_display(struct Mesh *mesh);
|
||||
void BKE_mesh_calc_normals_looptri(const struct MVert *mverts,
|
||||
int numVerts,
|
||||
const struct MLoop *mloop,
|
||||
const struct MLoopTri *looptri,
|
||||
int looptri_num,
|
||||
float (*r_tri_nors)[3]);
|
||||
|
||||
/**
|
||||
* Define sharp edges as needed to mimic 'autosmooth' from angle threshold.
|
||||
|
@ -987,10 +965,10 @@ void BKE_mesh_eval_geometry(struct Depsgraph *depsgraph, struct Mesh *mesh);
|
|||
|
||||
/* Draw Cache */
|
||||
void BKE_mesh_batch_cache_dirty_tag(struct Mesh *me, eMeshBatchDirtyMode mode);
|
||||
void BKE_mesh_batch_cache_free(struct Mesh *me);
|
||||
void BKE_mesh_batch_cache_free(void *batch_cache);
|
||||
|
||||
extern void (*BKE_mesh_batch_cache_dirty_tag_cb)(struct Mesh *me, eMeshBatchDirtyMode mode);
|
||||
extern void (*BKE_mesh_batch_cache_free_cb)(struct Mesh *me);
|
||||
extern void (*BKE_mesh_batch_cache_free_cb)(void *batch_cache);
|
||||
|
||||
/* mesh_debug.c */
|
||||
|
||||
|
|
|
@ -8,14 +8,12 @@
|
|||
* This file contains access functions for the Mesh.runtime struct.
|
||||
*/
|
||||
|
||||
//#include "BKE_customdata.h" /* for eCustomDataMask */
|
||||
#include "BKE_mesh_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct CustomData;
|
||||
struct CustomData_MeshMasks;
|
||||
struct Depsgraph;
|
||||
struct KeyBlock;
|
||||
|
@ -26,31 +24,44 @@ struct Mesh;
|
|||
struct Object;
|
||||
struct Scene;
|
||||
|
||||
/**
|
||||
* \brief Free all data (and mutexes) inside the runtime of the given mesh.
|
||||
*/
|
||||
void BKE_mesh_runtime_free_data(struct Mesh *mesh);
|
||||
|
||||
/** Return the number of derived triangles (looptris). */
|
||||
int BKE_mesh_runtime_looptri_len(const struct Mesh *mesh);
|
||||
void BKE_mesh_runtime_looptri_recalc(struct Mesh *mesh);
|
||||
|
||||
/**
|
||||
* \note This function only fills a cache, and therefore the mesh argument can
|
||||
* be considered logically const. Concurrent access is protected by a mutex.
|
||||
* \note This is a ported copy of dm_getLoopTriArray(dm).
|
||||
* Return mesh triangulation data, calculated lazily when necessary necessary.
|
||||
* See #MLoopTri for further description of mesh triangulation.
|
||||
*
|
||||
* \note Prefer #Mesh::looptris() in C++ code.
|
||||
*/
|
||||
const struct MLoopTri *BKE_mesh_runtime_looptri_ensure(const struct Mesh *mesh);
|
||||
|
||||
bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh);
|
||||
bool BKE_mesh_runtime_clear_edit_data(struct Mesh *mesh);
|
||||
bool BKE_mesh_runtime_reset_edit_data(struct Mesh *mesh);
|
||||
void BKE_mesh_runtime_clear_geometry(struct Mesh *mesh);
|
||||
void BKE_mesh_runtime_reset_edit_data(struct Mesh *mesh);
|
||||
|
||||
/**
|
||||
* \brief This function clears runtime cache of the given mesh.
|
||||
* Clear and free any derived caches associated with the mesh geometry data. Examples include BVH
|
||||
* caches, normals, triangulation, etc. This should be called when replacing a mesh's geometry
|
||||
* directly or making other large changes to topology. It does not need to be called on new meshes.
|
||||
*
|
||||
* Call this function to recalculate runtime data when used.
|
||||
* For "smaller" changes to meshes like updating positions, consider calling a more specific update
|
||||
* function like #BKE_mesh_tag_coords_changed.
|
||||
*
|
||||
* Also note that some derived caches like #CD_NORMAL and #CD_TANGENT are stored directly in
|
||||
* #CustomData.
|
||||
*/
|
||||
void BKE_mesh_runtime_clear_geometry(struct Mesh *mesh);
|
||||
|
||||
/**
|
||||
* Similar to #BKE_mesh_runtime_clear_geometry, but subtly different in that it also clears
|
||||
* data-block level features like evaluated data-blocks and edit mode data. They will be
|
||||
* functionally the same in most cases, but prefer this function if unsure, since it clears
|
||||
* more data.
|
||||
*/
|
||||
void BKE_mesh_runtime_clear_cache(struct Mesh *mesh);
|
||||
|
||||
/* This is a copy of DM_verttri_from_looptri(). */
|
||||
/**
|
||||
* Convert triangles encoded as face corner indices to triangles encoded as vertex indices.
|
||||
*/
|
||||
void BKE_mesh_runtime_verttri_from_looptri(struct MVertTri *r_verttri,
|
||||
const struct MLoop *mloop,
|
||||
const struct MLoopTri *looptri,
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
|
||||
# include "DNA_customdata_types.h"
|
||||
|
||||
# include "BLI_bounds_types.hh"
|
||||
# include "BLI_math_vec_types.hh"
|
||||
# include "BLI_shared_cache.hh"
|
||||
|
||||
# include "MEM_guardedalloc.h"
|
||||
|
||||
struct BVHCache;
|
||||
|
@ -84,6 +88,12 @@ struct MeshRuntime {
|
|||
/** Needed to ensure some thread-safety during render data pre-processing. */
|
||||
std::mutex render_mutex;
|
||||
|
||||
/**
|
||||
* A cache of bounds shared between data-blocks with unchanged positions. When changing positions
|
||||
* affect the bounds, the cache is "un-shared" with other geometries. See #SharedCache comments.
|
||||
*/
|
||||
SharedCache<Bounds<float3>> bounds_cache;
|
||||
|
||||
/** Lazily initialized SoA data from the #edit_mesh field in #Mesh. */
|
||||
EditMeshData *edit_data = nullptr;
|
||||
|
||||
|
@ -139,8 +149,8 @@ struct MeshRuntime {
|
|||
* #CustomData because they can be calculated on a `const` mesh, and adding custom data layers on
|
||||
* a `const` mesh is not thread-safe.
|
||||
*/
|
||||
bool vert_normals_dirty = false;
|
||||
bool poly_normals_dirty = false;
|
||||
bool vert_normals_dirty = true;
|
||||
bool poly_normals_dirty = true;
|
||||
float (*vert_normals)[3] = nullptr;
|
||||
float (*poly_normals)[3] = nullptr;
|
||||
|
||||
|
@ -151,8 +161,7 @@ struct MeshRuntime {
|
|||
uint32_t *subsurf_face_dot_tags = nullptr;
|
||||
|
||||
MeshRuntime() = default;
|
||||
/** \warning This does not free all data currently. See #BKE_mesh_runtime_free_data. */
|
||||
~MeshRuntime() = default;
|
||||
~MeshRuntime();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("MeshRuntime")
|
||||
};
|
||||
|
|
|
@ -861,7 +861,6 @@ void nodeSocketDeclarationsUpdate(struct bNode *node);
|
|||
/**
|
||||
* Node Clipboard.
|
||||
*/
|
||||
void BKE_node_clipboard_init(const struct bNodeTree *ntree);
|
||||
void BKE_node_clipboard_clear(void);
|
||||
void BKE_node_clipboard_free(void);
|
||||
/**
|
||||
|
@ -872,7 +871,6 @@ void BKE_node_clipboard_add_node(struct bNode *node);
|
|||
void BKE_node_clipboard_add_link(struct bNodeLink *link);
|
||||
const struct ListBase *BKE_node_clipboard_get_nodes(void);
|
||||
const struct ListBase *BKE_node_clipboard_get_links(void);
|
||||
int BKE_node_clipboard_get_type(void);
|
||||
|
||||
/**
|
||||
* Node Instance Hash.
|
||||
|
|
|
@ -6,6 +6,15 @@
|
|||
* \ingroup bke
|
||||
* \brief General operations for point clouds.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <mutex>
|
||||
|
||||
# include "BLI_bounds_types.hh"
|
||||
# include "BLI_math_vec_types.hh"
|
||||
# include "BLI_shared_cache.hh"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -22,6 +31,23 @@ struct Scene;
|
|||
extern const char *POINTCLOUD_ATTR_POSITION;
|
||||
extern const char *POINTCLOUD_ATTR_RADIUS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace blender::bke {
|
||||
|
||||
struct PointCloudRuntime {
|
||||
/**
|
||||
* A cache of bounds shared between data-blocks with unchanged positions and radii.
|
||||
* When data changes affect the bounds, the cache is "un-shared" with other geometries.
|
||||
* See #SharedCache comments.
|
||||
*/
|
||||
mutable SharedCache<Bounds<float3>> bounds_cache;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("PointCloudRuntime");
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
#endif
|
||||
|
||||
void *BKE_pointcloud_add(struct Main *bmain, const char *name);
|
||||
void *BKE_pointcloud_add_default(struct Main *bmain, const char *name);
|
||||
struct PointCloud *BKE_pointcloud_new_nomain(int totpoint);
|
||||
|
@ -30,7 +56,6 @@ void BKE_pointcloud_nomain_to_pointcloud(struct PointCloud *pointcloud_src,
|
|||
bool take_ownership);
|
||||
|
||||
struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);
|
||||
bool BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]);
|
||||
|
||||
bool BKE_pointcloud_attribute_required(const struct PointCloud *pointcloud, const char *name);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ typedef struct ShrinkwrapBoundaryData {
|
|||
/**
|
||||
* Free boundary data for target project.
|
||||
*/
|
||||
void BKE_shrinkwrap_discard_boundary_data(struct Mesh *mesh);
|
||||
void BKE_shrinkwrap_boundary_data_free(ShrinkwrapBoundaryData *data);
|
||||
void BKE_shrinkwrap_compute_boundary_data(struct Mesh *mesh);
|
||||
|
||||
/* Information about a mesh and BVH tree. */
|
||||
|
|
|
@ -49,6 +49,9 @@ typedef struct SubsurfRuntimeData {
|
|||
int stats_totloop;
|
||||
} SubsurfRuntimeData;
|
||||
|
||||
SubdivSettings BKE_subsurf_modifier_settings_init(const struct SubsurfModifierData *smd,
|
||||
bool use_render_params);
|
||||
|
||||
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,
|
||||
|
|
|
@ -665,7 +665,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
|
|||
* constructive modifier is executed, or a deform modifier needs normals
|
||||
* or certain data layers. */
|
||||
Mesh *mesh_input = (Mesh *)ob->data;
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh_input);
|
||||
Mesh *mesh_final = nullptr;
|
||||
Mesh *mesh_deform = nullptr;
|
||||
/* This geometry set contains the non-mesh data that might be generated by modifiers. */
|
||||
|
@ -1783,8 +1782,6 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
|
|||
mesh_eval = BKE_object_get_evaluated_mesh(ob);
|
||||
}
|
||||
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh_eval);
|
||||
|
||||
return mesh_eval;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
#include "BLI_blenlib.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_lib_id.h"
|
||||
|
@ -84,6 +86,8 @@ void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id)
|
|||
ID_BLEND_PATH(bpath_data->bmain, id) :
|
||||
NULL;
|
||||
bpath_data->absolute_base_path = absbase;
|
||||
bpath_data->owner_id = id;
|
||||
bpath_data->is_path_modified = false;
|
||||
|
||||
if ((flag & BKE_BPATH_FOREACH_PATH_SKIP_LINKED) && ID_IS_LINKED(id)) {
|
||||
return;
|
||||
|
@ -107,6 +111,10 @@ void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id)
|
|||
}
|
||||
|
||||
id_type->foreach_path(id, bpath_data);
|
||||
|
||||
if (bpath_data->is_path_modified) {
|
||||
DEG_id_tag_update(id, ID_RECALC_SOURCE | ID_RECALC_COPY_ON_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data)
|
||||
|
@ -140,6 +148,7 @@ bool BKE_bpath_foreach_path_fixed_process(BPathForeachPathData *bpath_data, char
|
|||
|
||||
if (bpath_data->callback_function(bpath_data, path_dst, path_src)) {
|
||||
BLI_strncpy(path, path_dst, FILE_MAX);
|
||||
bpath_data->is_path_modified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -166,6 +175,7 @@ bool BKE_bpath_foreach_path_dirfile_fixed_process(BPathForeachPathData *bpath_da
|
|||
|
||||
if (bpath_data->callback_function(bpath_data, path_dst, (const char *)path_src)) {
|
||||
BLI_split_dirfile(path_dst, path_dir, path_file, FILE_MAXDIR, FILE_MAXFILE);
|
||||
bpath_data->is_path_modified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -192,6 +202,7 @@ bool BKE_bpath_foreach_path_allocated_process(BPathForeachPathData *bpath_data,
|
|||
if (bpath_data->callback_function(bpath_data, path_dst, path_src)) {
|
||||
MEM_freeN(*path);
|
||||
(*path) = BLI_strdup(path_dst);
|
||||
bpath_data->is_path_modified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,12 @@ namespace blender::bke::curves::catmull_rom {
|
|||
int calculate_evaluated_num(const int points_num, const bool cyclic, const int resolution)
|
||||
{
|
||||
const int eval_num = resolution * segments_num(points_num, cyclic);
|
||||
if (cyclic) {
|
||||
/* Make sure there is a single evaluated point for the single-point curve case. */
|
||||
return std::max(eval_num, 1);
|
||||
}
|
||||
/* If the curve isn't cyclic, one last point is added to the final point. */
|
||||
return cyclic ? eval_num : eval_num + 1;
|
||||
return eval_num + 1;
|
||||
}
|
||||
|
||||
/* Adapted from Cycles #catmull_rom_basis_eval function. */
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "DNA_material_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_base.h"
|
||||
|
@ -94,6 +93,7 @@ static void curves_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, con
|
|||
dst.runtime = MEM_new<bke::CurvesGeometryRuntime>(__func__);
|
||||
|
||||
dst.runtime->type_counts = src.runtime->type_counts;
|
||||
dst.runtime->bounds_cache = src.runtime->bounds_cache;
|
||||
|
||||
curves_dst->batch_cache = nullptr;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
|
|||
|
||||
/* Though type counts are a cache, they must be copied because they are calculated eagerly. */
|
||||
dst.runtime->type_counts = src.runtime->type_counts;
|
||||
dst.runtime->bounds_cache = src.runtime->bounds_cache;
|
||||
}
|
||||
|
||||
CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
|
||||
|
@ -918,20 +919,22 @@ void CurvesGeometry::tag_positions_changed()
|
|||
this->runtime->tangent_cache_mutex.tag_dirty();
|
||||
this->runtime->normal_cache_mutex.tag_dirty();
|
||||
this->runtime->length_cache_mutex.tag_dirty();
|
||||
this->runtime->bounds_cache.tag_dirty();
|
||||
}
|
||||
void CurvesGeometry::tag_topology_changed()
|
||||
{
|
||||
this->runtime->position_cache_mutex.tag_dirty();
|
||||
this->runtime->tangent_cache_mutex.tag_dirty();
|
||||
this->runtime->normal_cache_mutex.tag_dirty();
|
||||
this->tag_positions_changed();
|
||||
this->runtime->offsets_cache_mutex.tag_dirty();
|
||||
this->runtime->nurbs_basis_cache_mutex.tag_dirty();
|
||||
this->runtime->length_cache_mutex.tag_dirty();
|
||||
}
|
||||
void CurvesGeometry::tag_normals_changed()
|
||||
{
|
||||
this->runtime->normal_cache_mutex.tag_dirty();
|
||||
}
|
||||
void CurvesGeometry::tag_radii_changed()
|
||||
{
|
||||
this->runtime->bounds_cache.tag_dirty();
|
||||
}
|
||||
|
||||
static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
|
||||
{
|
||||
|
@ -1006,25 +1009,28 @@ void CurvesGeometry::transform(const float4x4 &matrix)
|
|||
this->tag_positions_changed();
|
||||
}
|
||||
|
||||
static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves)
|
||||
{
|
||||
const Span<float3> positions = curves.positions();
|
||||
const VArray<float> radii = curves.attributes().lookup_or_default<float>(
|
||||
ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f);
|
||||
if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) {
|
||||
return bounds::min_max_with_radii(positions, radii.get_internal_span());
|
||||
}
|
||||
return bounds::min_max(positions);
|
||||
}
|
||||
|
||||
bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
|
||||
{
|
||||
const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this);
|
||||
if (!bounds) {
|
||||
if (this->points_num() == 0) {
|
||||
return false;
|
||||
}
|
||||
min = math::min(bounds->min, min);
|
||||
max = math::max(bounds->max, max);
|
||||
|
||||
this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
|
||||
const Span<float3> positions = this->evaluated_positions();
|
||||
if (this->attributes().contains("radius")) {
|
||||
const VArraySpan<float> radii = this->attributes().lookup<float>("radius");
|
||||
Array<float> evaluated_radii(this->evaluated_points_num());
|
||||
this->interpolate_to_evaluated(radii, evaluated_radii.as_mutable_span());
|
||||
r_bounds = *bounds::min_max_with_radii(positions, evaluated_radii.as_span());
|
||||
}
|
||||
else {
|
||||
r_bounds = *bounds::min_max(positions);
|
||||
}
|
||||
});
|
||||
|
||||
const Bounds<float3> &bounds = this->runtime->bounds_cache.data();
|
||||
min = math::min(bounds.min, min);
|
||||
max = math::max(bounds.max, max);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ bool BKE_editmesh_cache_calc_minmax(struct BMEditMesh *em,
|
|||
if (bm->totvert) {
|
||||
if (emd->vertexCos) {
|
||||
Span<float3> vert_coords(reinterpret_cast<const float3 *>(emd->vertexCos), bm->totvert);
|
||||
std::optional<bounds::MinMaxResult<float3>> bounds = bounds::min_max(vert_coords);
|
||||
std::optional<Bounds<float3>> bounds = bounds::min_max(vert_coords);
|
||||
BLI_assert(bounds.has_value());
|
||||
copy_v3_v3(min, math::min(bounds->min, float3(min)));
|
||||
copy_v3_v3(max, math::max(bounds->max, float3(max)));
|
||||
|
|
|
@ -315,6 +315,12 @@ static void tag_component_positions_changed(void *owner)
|
|||
curves.tag_positions_changed();
|
||||
}
|
||||
|
||||
static void tag_component_radii_changed(void *owner)
|
||||
{
|
||||
blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
|
||||
curves.tag_radii_changed();
|
||||
}
|
||||
|
||||
static void tag_component_normals_changed(void *owner)
|
||||
{
|
||||
blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
|
||||
|
@ -384,7 +390,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||
point_access,
|
||||
make_array_read_attribute<float>,
|
||||
make_array_write_attribute<float>,
|
||||
nullptr);
|
||||
tag_component_radii_changed);
|
||||
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
ATTR_DOMAIN_POINT,
|
||||
|
|
|
@ -105,6 +105,18 @@ void PointCloudComponent::ensure_owns_direct_data()
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
static void tag_component_positions_changed(void *owner)
|
||||
{
|
||||
PointCloud &points = *static_cast<PointCloud *>(owner);
|
||||
points.tag_positions_changed();
|
||||
}
|
||||
|
||||
static void tag_component_radius_changed(void *owner)
|
||||
{
|
||||
PointCloud &points = *static_cast<PointCloud *>(owner);
|
||||
points.tag_radii_changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* In this function all the attribute providers for a point cloud component are created. Most data
|
||||
* in this function is statically allocated, because it does not change over time.
|
||||
|
@ -135,7 +147,7 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
|||
point_access,
|
||||
make_array_read_attribute<float3>,
|
||||
make_array_write_attribute<float3>,
|
||||
nullptr);
|
||||
tag_component_positions_changed);
|
||||
static BuiltinCustomDataLayerProvider radius("radius",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT,
|
||||
|
@ -146,7 +158,7 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
|||
point_access,
|
||||
make_array_read_attribute<float>,
|
||||
make_array_write_attribute<float>,
|
||||
nullptr);
|
||||
tag_component_radius_changed);
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_INT32,
|
||||
|
|
|
@ -217,7 +217,7 @@ bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
|
|||
using namespace blender;
|
||||
bool have_minmax = false;
|
||||
if (const PointCloud *pointcloud = this->get_pointcloud_for_read()) {
|
||||
have_minmax |= BKE_pointcloud_minmax(pointcloud, *r_min, *r_max);
|
||||
have_minmax |= pointcloud->bounds_min_max(*r_min, *r_max);
|
||||
}
|
||||
if (const Mesh *mesh = this->get_mesh_for_read()) {
|
||||
have_minmax |= BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max);
|
||||
|
@ -227,14 +227,7 @@ bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
|
|||
}
|
||||
if (const Curves *curves_id = this->get_curves_for_read()) {
|
||||
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
|
||||
/* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
|
||||
std::optional<bounds::MinMaxResult<float3>> min_max = bounds::min_max(
|
||||
curves.evaluated_positions());
|
||||
if (min_max) {
|
||||
have_minmax = true;
|
||||
*r_min = math::min(*r_min, min_max->min);
|
||||
*r_max = math::max(*r_max, min_max->max);
|
||||
}
|
||||
have_minmax |= curves.bounds_min_max(*r_min, *r_max);
|
||||
}
|
||||
return have_minmax;
|
||||
}
|
||||
|
|
|
@ -684,6 +684,15 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
|
|||
return;
|
||||
}
|
||||
|
||||
if (ELEM(GS(id->name), ID_IM)) {
|
||||
/* Images which have a 'viewer' source (e.g. render results) should not be considered as
|
||||
* orphaned/unused data. */
|
||||
Image *image = (Image *)id;
|
||||
if (image->source == IMA_SRC_VIEWER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* An ID user is 'valid' (i.e. may affect the 'used'/'not used' status of the ID it uses) if it
|
||||
* does not match `ignored_usages`, and does match `required_usages`. */
|
||||
const int ignored_usages = (IDWALK_CB_LOOPBACK | IDWALK_CB_EMBEDDED);
|
||||
|
@ -696,11 +705,10 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
|
|||
bool has_valid_from_users = false;
|
||||
/* Preemptively consider this ID as unused. That way if there is a loop of dependency leading
|
||||
* back to it, it won't create a fake 'valid user' detection.
|
||||
* NOTE: This can only be done for a subset of IDs, some types are never 'indirectly unused',
|
||||
* same for IDs with a fake user. */
|
||||
if ((id->flag & LIB_FAKEUSER) == 0 && !ELEM(GS(id->name), ID_SCE, ID_WM, ID_SCR, ID_WS, ID_LI)) {
|
||||
id->tag |= tag;
|
||||
}
|
||||
* NOTE: there are some cases (like when fake user is set, or some ID types) which are never
|
||||
* 'indirectly unused'. However, these have already been checked and early-returned above, so any
|
||||
* ID reaching this point of the function can be tagged. */
|
||||
id->tag |= tag;
|
||||
for (MainIDRelationsEntryItem *id_from_item = id_relations->from_ids; id_from_item != NULL;
|
||||
id_from_item = id_from_item->next) {
|
||||
if ((id_from_item->usage_flag & ignored_usages) != 0 ||
|
||||
|
@ -727,8 +735,7 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
|
|||
id->tag &= ~tag;
|
||||
}
|
||||
else {
|
||||
/* This ID has no 'valid' users, tag it as unused. */
|
||||
id->tag |= tag;
|
||||
/* This ID has no 'valid' users, its 'unused' tag preemptively set above can be kept. */
|
||||
if (r_num_tagged != NULL) {
|
||||
r_num_tagged[INDEX_ID_NULL]++;
|
||||
r_num_tagged[BKE_idtype_idcode_to_index(GS(id->name))]++;
|
||||
|
|
|
@ -1499,7 +1499,10 @@ Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob)
|
|||
for (int i = 0; i < mesh->totvert; i++) {
|
||||
normalize_v3(process.no[i]);
|
||||
}
|
||||
mesh->runtime->vert_normals = process.no;
|
||||
memcpy(BKE_mesh_vertex_normals_for_write(mesh),
|
||||
process.no,
|
||||
sizeof(float[3]) * size_t(mesh->totvert));
|
||||
MEM_freeN(process.no);
|
||||
BKE_mesh_vertex_normals_clear_dirty(mesh);
|
||||
|
||||
mesh->totloop = loop_offset;
|
||||
|
|
|
@ -91,10 +91,6 @@ static void mesh_init_data(ID *id)
|
|||
|
||||
mesh->runtime = new blender::bke::MeshRuntime();
|
||||
|
||||
/* A newly created mesh does not have normals, so tag them dirty. This will be cleared
|
||||
* by #BKE_mesh_vertex_normals_clear_dirty or #BKE_mesh_poly_normals_ensure. */
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
|
||||
mesh->face_sets_color_seed = BLI_hash_int(PIL_check_seconds_timer_i() & UINT_MAX);
|
||||
}
|
||||
|
||||
|
@ -128,6 +124,11 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
|||
* highly unlikely we want to create a duplicate and not use it for drawing. */
|
||||
mesh_dst->runtime->is_original_bmesh = false;
|
||||
|
||||
/* Share the bounding box cache between the source and destination mesh for improved performance
|
||||
* when the source is persistent and edits to the destination don't change the bounds. It will be
|
||||
* "un-shared" as necessary when the positions are changed. */
|
||||
mesh_dst->runtime->bounds_cache = mesh_src->runtime->bounds_cache;
|
||||
|
||||
/* Only do tessface if we have no polys. */
|
||||
const bool do_tessface = ((mesh_src->totface != 0) && (mesh_src->totpoly == 0));
|
||||
|
||||
|
@ -158,21 +159,12 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
|||
|
||||
mesh_dst->mselect = (MSelect *)MEM_dupallocN(mesh_dst->mselect);
|
||||
|
||||
/* Set normal layers dirty. They should be dirty by default on new meshes anyway, but being
|
||||
* explicit about it is safer. Alternatively normal layers could be copied if they aren't dirty,
|
||||
* avoiding recomputation in some cases. However, a copied mesh is often changed anyway, so that
|
||||
* idea is not clearly better. With proper reference counting, all custom data layers could be
|
||||
* copied as the cost would be much lower. */
|
||||
BKE_mesh_normals_tag_dirty(mesh_dst);
|
||||
|
||||
/* TODO: Do we want to add flag to prevent this? */
|
||||
if (mesh_src->key && (flag & LIB_ID_COPY_SHAPEKEY)) {
|
||||
BKE_id_copy_ex(bmain, &mesh_src->key->id, (ID **)&mesh_dst->key, flag);
|
||||
/* XXX This is not nice, we need to make BKE_id_copy_ex fully re-entrant... */
|
||||
mesh_dst->key->from = &mesh_dst->id;
|
||||
}
|
||||
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh_dst);
|
||||
}
|
||||
|
||||
void BKE_mesh_free_editmesh(struct Mesh *mesh)
|
||||
|
@ -196,7 +188,6 @@ static void mesh_free_data(ID *id)
|
|||
|
||||
BKE_mesh_free_editmesh(mesh);
|
||||
|
||||
BKE_mesh_runtime_free_data(mesh);
|
||||
mesh_clear_geometry(mesh);
|
||||
MEM_SAFE_FREE(mesh->mat);
|
||||
|
||||
|
@ -359,10 +350,6 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
|
|||
BLI_endian_switch_uint32_array(tf->col, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/* We don't expect to load normals from files, since they are derived data. */
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
|
||||
}
|
||||
|
||||
static void mesh_blend_read_lib(BlendLibReader *reader, ID *id)
|
||||
|
@ -995,8 +982,6 @@ void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src)
|
|||
|
||||
BKE_mesh_copy_parameters(me_dst, me_src);
|
||||
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(me_dst);
|
||||
|
||||
/* Copy vertex group names. */
|
||||
BLI_assert(BLI_listbase_is_empty(&me_dst->vertex_group_names));
|
||||
BKE_defgroup_copy_list(&me_dst->vertex_group_names, &me_src->vertex_group_names);
|
||||
|
@ -1523,29 +1508,27 @@ bool BKE_mesh_minmax(const Mesh *me, float r_min[3], float r_max[3])
|
|||
return false;
|
||||
}
|
||||
|
||||
struct Result {
|
||||
float3 min;
|
||||
float3 max;
|
||||
};
|
||||
const Span<MVert> verts = me->verts();
|
||||
me->runtime->bounds_cache.ensure([me](Bounds<float3> &r_bounds) {
|
||||
const Span<MVert> verts = me->verts();
|
||||
r_bounds = threading::parallel_reduce(
|
||||
verts.index_range(),
|
||||
1024,
|
||||
Bounds<float3>{float3(FLT_MAX), float3(-FLT_MAX)},
|
||||
[verts](IndexRange range, const Bounds<float3> &init) {
|
||||
Bounds<float3> result = init;
|
||||
for (const int i : range) {
|
||||
math::min_max(float3(verts[i].co), result.min, result.max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const Bounds<float3> &a, const Bounds<float3> &b) {
|
||||
return Bounds<float3>{math::min(a.min, b.min), math::max(a.max, b.max)};
|
||||
});
|
||||
});
|
||||
|
||||
const Result minmax = threading::parallel_reduce(
|
||||
verts.index_range(),
|
||||
1024,
|
||||
Result{float3(FLT_MAX), float3(-FLT_MAX)},
|
||||
[verts](IndexRange range, const Result &init) {
|
||||
Result result = init;
|
||||
for (const int i : range) {
|
||||
math::min_max(float3(verts[i].co), result.min, result.max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const Result &a, const Result &b) {
|
||||
return Result{math::min(a.min, b.min), math::max(a.max, b.max)};
|
||||
});
|
||||
|
||||
copy_v3_v3(r_min, math::min(minmax.min, float3(r_min)));
|
||||
copy_v3_v3(r_max, math::max(minmax.max, float3(r_max)));
|
||||
const Bounds<float3> &bounds = me->runtime->bounds_cache.data();
|
||||
copy_v3_v3(r_min, math::min(bounds.min, float3(r_min)));
|
||||
copy_v3_v3(r_max, math::max(bounds.max, float3(r_max)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1832,8 +1815,6 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh,
|
|||
nullptr,
|
||||
r_lnors_spacearr,
|
||||
clnors);
|
||||
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
|
||||
}
|
||||
|
||||
void BKE_mesh_calc_normals_split(Mesh *mesh)
|
||||
|
@ -2100,7 +2081,6 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
|
|||
/* Also frees new_verts/edges temp data, since we used its memarena to allocate them. */
|
||||
BKE_lnor_spacearr_free(&lnors_spacearr);
|
||||
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
|
||||
#ifdef VALIDATE_MESH
|
||||
BKE_mesh_validate(mesh, true, true);
|
||||
#endif
|
||||
|
|
|
@ -128,13 +128,13 @@ float (*BKE_mesh_poly_normals_for_write(Mesh *mesh))[3]
|
|||
void BKE_mesh_vertex_normals_clear_dirty(Mesh *mesh)
|
||||
{
|
||||
mesh->runtime->vert_normals_dirty = false;
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
|
||||
BLI_assert(mesh->runtime->vert_normals || mesh->totvert == 0);
|
||||
}
|
||||
|
||||
void BKE_mesh_poly_normals_clear_dirty(Mesh *mesh)
|
||||
{
|
||||
mesh->runtime->poly_normals_dirty = false;
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
|
||||
BLI_assert(mesh->runtime->poly_normals || mesh->totpoly == 0);
|
||||
}
|
||||
|
||||
bool BKE_mesh_vertex_normals_are_dirty(const Mesh *mesh)
|
||||
|
@ -147,25 +147,6 @@ bool BKE_mesh_poly_normals_are_dirty(const Mesh *mesh)
|
|||
return mesh->runtime->poly_normals_dirty;
|
||||
}
|
||||
|
||||
void BKE_mesh_clear_derived_normals(Mesh *mesh)
|
||||
{
|
||||
MEM_SAFE_FREE(mesh->runtime->vert_normals);
|
||||
MEM_SAFE_FREE(mesh->runtime->poly_normals);
|
||||
|
||||
mesh->runtime->vert_normals_dirty = true;
|
||||
mesh->runtime->poly_normals_dirty = true;
|
||||
}
|
||||
|
||||
void BKE_mesh_assert_normals_dirty_or_calculated(const Mesh *mesh)
|
||||
{
|
||||
if (!mesh->runtime->vert_normals_dirty) {
|
||||
BLI_assert(mesh->runtime->vert_normals || mesh->totvert == 0);
|
||||
}
|
||||
if (!mesh->runtime->poly_normals_dirty) {
|
||||
BLI_assert(mesh->runtime->poly_normals || mesh->totpoly == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -463,60 +444,6 @@ void BKE_mesh_calc_normals(Mesh *mesh)
|
|||
BKE_mesh_vertex_normals_ensure(mesh);
|
||||
}
|
||||
|
||||
void BKE_mesh_calc_normals_looptri(const MVert *mverts,
|
||||
int numVerts,
|
||||
const MLoop *mloop,
|
||||
const MLoopTri *looptri,
|
||||
int looptri_num,
|
||||
float (*r_tri_nors)[3])
|
||||
{
|
||||
float(*tnorms)[3] = (float(*)[3])MEM_calloc_arrayN(size_t(numVerts), sizeof(*tnorms), "tnorms");
|
||||
float(*fnors)[3] = (r_tri_nors) ? r_tri_nors :
|
||||
(float(*)[3])MEM_calloc_arrayN(
|
||||
size_t(looptri_num), sizeof(*fnors), "meshnormals");
|
||||
|
||||
if (!tnorms || !fnors) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < looptri_num; i++) {
|
||||
const MLoopTri *lt = &looptri[i];
|
||||
float *f_no = fnors[i];
|
||||
const uint vtri[3] = {
|
||||
mloop[lt->tri[0]].v,
|
||||
mloop[lt->tri[1]].v,
|
||||
mloop[lt->tri[2]].v,
|
||||
};
|
||||
|
||||
normal_tri_v3(f_no, mverts[vtri[0]].co, mverts[vtri[1]].co, mverts[vtri[2]].co);
|
||||
|
||||
accumulate_vertex_normals_tri_v3(tnorms[vtri[0]],
|
||||
tnorms[vtri[1]],
|
||||
tnorms[vtri[2]],
|
||||
f_no,
|
||||
mverts[vtri[0]].co,
|
||||
mverts[vtri[1]].co,
|
||||
mverts[vtri[2]].co);
|
||||
}
|
||||
|
||||
/* Following Mesh convention; we use vertex coordinate itself for normal in this case. */
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
const MVert *mv = &mverts[i];
|
||||
float *no = tnorms[i];
|
||||
|
||||
if (UNLIKELY(normalize_v3(no) == 0.0f)) {
|
||||
normalize_v3_v3(no, mv->co);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
MEM_freeN(tnorms);
|
||||
|
||||
if (fnors != r_tri_nors) {
|
||||
MEM_freeN(fnors);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr,
|
||||
const int numLoops,
|
||||
const char data_type)
|
||||
|
|
|
@ -31,24 +31,84 @@ using blender::Span;
|
|||
/** \name Mesh Runtime Struct Utils
|
||||
* \{ */
|
||||
|
||||
void BKE_mesh_runtime_free_data(Mesh *mesh)
|
||||
namespace blender::bke {
|
||||
|
||||
static void edit_data_reset(EditMeshData &edit_data)
|
||||
{
|
||||
BKE_mesh_runtime_clear_cache(mesh);
|
||||
MEM_SAFE_FREE(edit_data.polyCos);
|
||||
MEM_SAFE_FREE(edit_data.polyNos);
|
||||
MEM_SAFE_FREE(edit_data.vertexCos);
|
||||
MEM_SAFE_FREE(edit_data.vertexNos);
|
||||
}
|
||||
|
||||
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
|
||||
static void free_edit_data(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
if (mesh->runtime->mesh_eval != nullptr) {
|
||||
mesh->runtime->mesh_eval->edit_mesh = nullptr;
|
||||
BKE_id_free(nullptr, mesh->runtime->mesh_eval);
|
||||
mesh->runtime->mesh_eval = nullptr;
|
||||
if (mesh_runtime.edit_data) {
|
||||
edit_data_reset(*mesh_runtime.edit_data);
|
||||
MEM_freeN(mesh_runtime.edit_data);
|
||||
mesh_runtime.edit_data = nullptr;
|
||||
}
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
BKE_mesh_batch_cache_free(mesh);
|
||||
BKE_mesh_runtime_clear_edit_data(mesh);
|
||||
BKE_mesh_clear_derived_normals(mesh);
|
||||
}
|
||||
|
||||
static void free_mesh_eval(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
if (mesh_runtime.mesh_eval != nullptr) {
|
||||
mesh_runtime.mesh_eval->edit_mesh = nullptr;
|
||||
BKE_id_free(nullptr, mesh_runtime.mesh_eval);
|
||||
mesh_runtime.mesh_eval = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_subdiv_ccg(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
/* TODO(sergey): Does this really belong here? */
|
||||
if (mesh_runtime.subdiv_ccg != nullptr) {
|
||||
BKE_subdiv_ccg_destroy(mesh_runtime.subdiv_ccg);
|
||||
mesh_runtime.subdiv_ccg = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_bvh_cache(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
if (mesh_runtime.bvh_cache) {
|
||||
bvhcache_free(mesh_runtime.bvh_cache);
|
||||
mesh_runtime.bvh_cache = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_normals(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
MEM_SAFE_FREE(mesh_runtime.vert_normals);
|
||||
MEM_SAFE_FREE(mesh_runtime.poly_normals);
|
||||
mesh_runtime.vert_normals_dirty = true;
|
||||
mesh_runtime.poly_normals_dirty = true;
|
||||
}
|
||||
|
||||
static void free_batch_cache(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
if (mesh_runtime.batch_cache) {
|
||||
BKE_mesh_batch_cache_free(mesh_runtime.batch_cache);
|
||||
mesh_runtime.batch_cache = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MeshRuntime::~MeshRuntime()
|
||||
{
|
||||
free_mesh_eval(*this);
|
||||
free_subdiv_ccg(*this);
|
||||
free_bvh_cache(*this);
|
||||
free_edit_data(*this);
|
||||
free_batch_cache(*this);
|
||||
free_normals(*this);
|
||||
if (this->shrinkwrap_data) {
|
||||
BKE_shrinkwrap_boundary_data_free(this->shrinkwrap_data);
|
||||
}
|
||||
MEM_SAFE_FREE(this->subsurf_face_dot_tags);
|
||||
MEM_SAFE_FREE(this->looptris.array);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
blender::Span<MLoopTri> Mesh::looptris() const
|
||||
{
|
||||
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(this);
|
||||
|
@ -90,7 +150,7 @@ static void mesh_ensure_looptri_data(Mesh *mesh)
|
|||
}
|
||||
}
|
||||
|
||||
void BKE_mesh_runtime_looptri_recalc(Mesh *mesh)
|
||||
static void recalc_loopris(Mesh *mesh)
|
||||
{
|
||||
mesh_ensure_looptri_data(mesh);
|
||||
BLI_assert(mesh->totpoly == 0 || mesh->runtime->looptris.array_wip != nullptr);
|
||||
|
@ -142,8 +202,7 @@ const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh)
|
|||
}
|
||||
else {
|
||||
/* Must isolate multithreaded tasks while holding a mutex lock. */
|
||||
blender::threading::isolate_task(
|
||||
[&]() { BKE_mesh_runtime_looptri_recalc(const_cast<Mesh *>(mesh)); });
|
||||
blender::threading::isolate_task([&]() { recalc_loopris(const_cast<Mesh *>(mesh)); });
|
||||
looptri = mesh->runtime->looptris.array;
|
||||
}
|
||||
|
||||
|
@ -167,76 +226,54 @@ bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh)
|
|||
if (mesh->runtime->edit_data != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh->runtime->edit_data = MEM_cnew<EditMeshData>(__func__);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
|
||||
void BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
|
||||
{
|
||||
EditMeshData *edit_data = mesh->runtime->edit_data;
|
||||
if (edit_data == nullptr) {
|
||||
return false;
|
||||
using namespace blender::bke;
|
||||
if (EditMeshData *edit_data = mesh->runtime->edit_data) {
|
||||
edit_data_reset(*edit_data);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(edit_data->polyCos);
|
||||
MEM_SAFE_FREE(edit_data->polyNos);
|
||||
MEM_SAFE_FREE(edit_data->vertexCos);
|
||||
MEM_SAFE_FREE(edit_data->vertexNos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BKE_mesh_runtime_clear_edit_data(Mesh *mesh)
|
||||
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
|
||||
{
|
||||
if (mesh->runtime->edit_data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
BKE_mesh_runtime_reset_edit_data(mesh);
|
||||
|
||||
MEM_freeN(mesh->runtime->edit_data);
|
||||
mesh->runtime->edit_data = nullptr;
|
||||
|
||||
return true;
|
||||
using namespace blender::bke;
|
||||
free_mesh_eval(*mesh->runtime);
|
||||
free_batch_cache(*mesh->runtime);
|
||||
free_edit_data(*mesh->runtime);
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
}
|
||||
|
||||
void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
|
||||
{
|
||||
BKE_mesh_tag_coords_changed(mesh);
|
||||
|
||||
/* TODO(sergey): Does this really belong here? */
|
||||
if (mesh->runtime->subdiv_ccg != nullptr) {
|
||||
BKE_subdiv_ccg_destroy(mesh->runtime->subdiv_ccg);
|
||||
mesh->runtime->subdiv_ccg = nullptr;
|
||||
/* Tagging shared caches dirty will free the allocated data if there is only one user. */
|
||||
free_bvh_cache(*mesh->runtime);
|
||||
free_normals(*mesh->runtime);
|
||||
free_subdiv_ccg(*mesh->runtime);
|
||||
mesh->runtime->bounds_cache.tag_dirty();
|
||||
if (mesh->runtime->shrinkwrap_data) {
|
||||
BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
|
||||
}
|
||||
BKE_shrinkwrap_discard_boundary_data(mesh);
|
||||
|
||||
MEM_SAFE_FREE(mesh->runtime->subsurf_face_dot_tags);
|
||||
MEM_SAFE_FREE(mesh->runtime->looptris.array);
|
||||
}
|
||||
|
||||
void BKE_mesh_tag_coords_changed(Mesh *mesh)
|
||||
{
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
free_bvh_cache(*mesh->runtime);
|
||||
MEM_SAFE_FREE(mesh->runtime->looptris.array);
|
||||
if (mesh->runtime->bvh_cache) {
|
||||
bvhcache_free(mesh->runtime->bvh_cache);
|
||||
mesh->runtime->bvh_cache = nullptr;
|
||||
}
|
||||
mesh->runtime->bounds_cache.tag_dirty();
|
||||
}
|
||||
|
||||
void BKE_mesh_tag_coords_changed_uniformly(Mesh *mesh)
|
||||
{
|
||||
const bool vert_normals_were_dirty = BKE_mesh_vertex_normals_are_dirty(mesh);
|
||||
const bool poly_normals_were_dirty = BKE_mesh_poly_normals_are_dirty(mesh);
|
||||
|
||||
BKE_mesh_tag_coords_changed(mesh);
|
||||
/* The normals didn't change, since all verts moved by the same amount. */
|
||||
if (!vert_normals_were_dirty) {
|
||||
BKE_mesh_vertex_normals_clear_dirty(mesh);
|
||||
}
|
||||
if (!poly_normals_were_dirty) {
|
||||
BKE_mesh_poly_normals_clear_dirty(mesh);
|
||||
}
|
||||
/* The normals and triangulation didn't change, since all verts moved by the same amount. */
|
||||
free_bvh_cache(*mesh->runtime);
|
||||
mesh->runtime->bounds_cache.tag_dirty();
|
||||
}
|
||||
|
||||
bool BKE_mesh_is_deformed_only(const Mesh *mesh)
|
||||
|
@ -257,7 +294,7 @@ eMeshWrapperType BKE_mesh_wrapper_type(const struct Mesh *mesh)
|
|||
|
||||
/* Draw Engine */
|
||||
void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = nullptr;
|
||||
void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = nullptr;
|
||||
void (*BKE_mesh_batch_cache_free_cb)(void *batch_cache) = nullptr;
|
||||
|
||||
void BKE_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
|
||||
{
|
||||
|
@ -265,11 +302,9 @@ void BKE_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
|
|||
BKE_mesh_batch_cache_dirty_tag_cb(me, mode);
|
||||
}
|
||||
}
|
||||
void BKE_mesh_batch_cache_free(Mesh *me)
|
||||
void BKE_mesh_batch_cache_free(void *batch_cache)
|
||||
{
|
||||
if (me->runtime->batch_cache) {
|
||||
BKE_mesh_batch_cache_free_cb(me);
|
||||
}
|
||||
BKE_mesh_batch_cache_free_cb(batch_cache);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -298,7 +298,6 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
|
|||
}
|
||||
|
||||
const float(*vert_normals)[3] = nullptr;
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
|
||||
if (!BKE_mesh_vertex_normals_are_dirty(mesh)) {
|
||||
vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
|
||||
}
|
||||
|
@ -1104,8 +1103,6 @@ bool BKE_mesh_is_valid(Mesh *me)
|
|||
bool is_valid = true;
|
||||
bool changed = true;
|
||||
|
||||
BKE_mesh_assert_normals_dirty_or_calculated(me);
|
||||
|
||||
is_valid &= BKE_mesh_validate_all_customdata(
|
||||
&me->vdata,
|
||||
me->totvert,
|
||||
|
@ -1141,6 +1138,13 @@ bool BKE_mesh_is_valid(Mesh *me)
|
|||
do_fixes,
|
||||
&changed);
|
||||
|
||||
if (!me->runtime->vert_normals_dirty) {
|
||||
BLI_assert(me->runtime->vert_normals || me->totvert == 0);
|
||||
}
|
||||
if (!me->runtime->poly_normals_dirty) {
|
||||
BLI_assert(me->runtime->poly_normals || me->totpoly == 0);
|
||||
}
|
||||
|
||||
BLI_assert(changed == false);
|
||||
|
||||
return is_valid;
|
||||
|
|
|
@ -3779,16 +3779,10 @@ struct bNodeClipboard {
|
|||
#endif
|
||||
|
||||
ListBase links;
|
||||
int type;
|
||||
};
|
||||
|
||||
static bNodeClipboard node_clipboard = {{nullptr}};
|
||||
|
||||
void BKE_node_clipboard_init(const struct bNodeTree *ntree)
|
||||
{
|
||||
node_clipboard.type = ntree->type;
|
||||
}
|
||||
|
||||
void BKE_node_clipboard_clear()
|
||||
{
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &node_clipboard.links) {
|
||||
|
@ -3894,11 +3888,6 @@ const ListBase *BKE_node_clipboard_get_links()
|
|||
return &node_clipboard.links;
|
||||
}
|
||||
|
||||
int BKE_node_clipboard_get_type()
|
||||
{
|
||||
return node_clipboard.type;
|
||||
}
|
||||
|
||||
void BKE_node_clipboard_free()
|
||||
{
|
||||
BKE_node_clipboard_validate();
|
||||
|
|
|
@ -3140,9 +3140,6 @@ void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], const int
|
|||
}
|
||||
}
|
||||
|
||||
/* coordinates are new -- normals should also be updated */
|
||||
BKE_mesh_calc_normals_looptri(
|
||||
pbvh->verts, pbvh->totvert, pbvh->mloop, pbvh->looptri, pbvh->totprim, NULL);
|
||||
|
||||
for (int a = 0; a < pbvh->totnode; a++) {
|
||||
BKE_pbvh_node_mark_update(&pbvh->nodes[a]);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "BLI_bounds.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_string.h"
|
||||
|
@ -68,6 +68,8 @@ static void pointcloud_init_data(ID *id)
|
|||
nullptr,
|
||||
pointcloud->totpoint,
|
||||
POINTCLOUD_ATTR_POSITION);
|
||||
|
||||
pointcloud->runtime = new blender::bke::PointCloudRuntime();
|
||||
}
|
||||
|
||||
static void pointcloud_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, const int flag)
|
||||
|
@ -83,6 +85,9 @@ static void pointcloud_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src,
|
|||
alloc_type,
|
||||
pointcloud_dst->totpoint);
|
||||
|
||||
pointcloud_dst->runtime = new blender::bke::PointCloudRuntime();
|
||||
pointcloud_dst->runtime->bounds_cache = pointcloud_src->runtime->bounds_cache;
|
||||
|
||||
pointcloud_dst->batch_cache = nullptr;
|
||||
}
|
||||
|
||||
|
@ -93,6 +98,7 @@ static void pointcloud_free_data(ID *id)
|
|||
BKE_pointcloud_batch_cache_free(pointcloud);
|
||||
CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
|
||||
MEM_SAFE_FREE(pointcloud->mat);
|
||||
delete pointcloud->runtime;
|
||||
}
|
||||
|
||||
static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data)
|
||||
|
@ -139,6 +145,8 @@ static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id)
|
|||
|
||||
/* Materials */
|
||||
BLO_read_pointer_array(reader, (void **)&pointcloud->mat);
|
||||
|
||||
pointcloud->runtime = new blender::bke::PointCloudRuntime();
|
||||
}
|
||||
|
||||
static void pointcloud_blend_read_lib(BlendLibReader *reader, ID *id)
|
||||
|
@ -277,33 +285,27 @@ void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src,
|
|||
}
|
||||
}
|
||||
|
||||
static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds(
|
||||
const PointCloud &pointcloud)
|
||||
{
|
||||
blender::bke::AttributeAccessor attributes = pointcloud.attributes();
|
||||
blender::VArraySpan<float3> positions = attributes.lookup_or_default<float3>(
|
||||
POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT, float3(0));
|
||||
blender::VArray<float> radii = attributes.lookup_or_default<float>(
|
||||
POINTCLOUD_ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f);
|
||||
|
||||
if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) {
|
||||
return blender::bounds::min_max_with_radii(positions, radii.get_internal_span());
|
||||
}
|
||||
return blender::bounds::min_max(positions);
|
||||
}
|
||||
|
||||
bool BKE_pointcloud_minmax(const PointCloud *pointcloud, float r_min[3], float r_max[3])
|
||||
bool PointCloud::bounds_min_max(blender::float3 &min, blender::float3 &max) const
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
const std::optional<bounds::MinMaxResult<float3>> min_max = point_cloud_bounds(*pointcloud);
|
||||
if (!min_max) {
|
||||
using namespace blender::bke;
|
||||
if (this->totpoint == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
copy_v3_v3(r_min, math::min(min_max->min, float3(r_min)));
|
||||
copy_v3_v3(r_max, math::max(min_max->max, float3(r_max)));
|
||||
|
||||
this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
|
||||
const AttributeAccessor attributes = this->attributes();
|
||||
const VArraySpan<float3> positions = attributes.lookup<float3>(POINTCLOUD_ATTR_POSITION);
|
||||
if (attributes.contains(POINTCLOUD_ATTR_RADIUS)) {
|
||||
const VArraySpan<float> radii = attributes.lookup<float>(POINTCLOUD_ATTR_RADIUS);
|
||||
r_bounds = *bounds::min_max_with_radii(positions, radii);
|
||||
}
|
||||
else {
|
||||
r_bounds = *bounds::min_max(positions);
|
||||
}
|
||||
});
|
||||
const Bounds<float3> &bounds = this->runtime->bounds_cache.data();
|
||||
min = math::min(bounds.min, min);
|
||||
max = math::max(bounds.max, max);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -326,7 +328,7 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
|
|||
}
|
||||
else {
|
||||
const PointCloud *pointcloud = static_cast<PointCloud *>(ob->data);
|
||||
BKE_pointcloud_minmax(pointcloud, min, max);
|
||||
pointcloud->bounds_min_max(min, max);
|
||||
}
|
||||
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
|
||||
|
||||
|
@ -427,6 +429,16 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene
|
|||
object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
|
||||
}
|
||||
|
||||
void PointCloud::tag_positions_changed()
|
||||
{
|
||||
this->runtime->bounds_cache.tag_dirty();
|
||||
}
|
||||
|
||||
void PointCloud::tag_radii_changed()
|
||||
{
|
||||
this->runtime->bounds_cache.tag_dirty();
|
||||
}
|
||||
|
||||
/* Draw Cache */
|
||||
|
||||
void (*BKE_pointcloud_batch_cache_dirty_tag_cb)(PointCloud *pointcloud, int mode) = nullptr;
|
||||
|
|
|
@ -152,20 +152,14 @@ void BKE_shrinkwrap_free_tree(ShrinkwrapTreeData *data)
|
|||
free_bvhtree_from_mesh(&data->treeData);
|
||||
}
|
||||
|
||||
void BKE_shrinkwrap_discard_boundary_data(Mesh *mesh)
|
||||
void BKE_shrinkwrap_boundary_data_free(ShrinkwrapBoundaryData *data)
|
||||
{
|
||||
ShrinkwrapBoundaryData *data = mesh->runtime->shrinkwrap_data;
|
||||
MEM_freeN((void *)data->edge_is_boundary);
|
||||
MEM_freeN((void *)data->looptri_has_boundary);
|
||||
MEM_freeN((void *)data->vert_boundary_id);
|
||||
MEM_freeN((void *)data->boundary_verts);
|
||||
|
||||
if (data != nullptr) {
|
||||
MEM_freeN((void *)data->edge_is_boundary);
|
||||
MEM_freeN((void *)data->looptri_has_boundary);
|
||||
MEM_freeN((void *)data->vert_boundary_id);
|
||||
MEM_freeN((void *)data->boundary_verts);
|
||||
|
||||
MEM_freeN(data);
|
||||
}
|
||||
|
||||
mesh->runtime->shrinkwrap_data = nullptr;
|
||||
MEM_freeN(data);
|
||||
}
|
||||
|
||||
/* Accumulate edge for average boundary edge direction. */
|
||||
|
@ -326,8 +320,9 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
|||
|
||||
void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh)
|
||||
{
|
||||
BKE_shrinkwrap_discard_boundary_data(mesh);
|
||||
|
||||
if (mesh->runtime->shrinkwrap_data) {
|
||||
BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
|
||||
}
|
||||
mesh->runtime->shrinkwrap_data = shrinkwrap_build_boundary_data(mesh);
|
||||
}
|
||||
|
||||
|
|
|
@ -1522,6 +1522,12 @@ void BKE_sound_jack_scene_update(Scene *scene, int mode, double time)
|
|||
void BKE_sound_evaluate(Depsgraph *depsgraph, Main *bmain, bSound *sound)
|
||||
{
|
||||
DEG_debug_print_eval(depsgraph, __func__, sound->id.name, sound);
|
||||
if (sound->id.recalc & ID_RECALC_SOURCE) {
|
||||
/* Sequencer checks this flag to see if the strip sound is to be updated from the Audaspace
|
||||
* side. */
|
||||
sound->id.recalc |= ID_RECALC_AUDIO;
|
||||
}
|
||||
|
||||
if (sound->id.recalc & ID_RECALC_AUDIO) {
|
||||
BKE_sound_load(bmain, sound);
|
||||
return;
|
||||
|
|
|
@ -20,11 +20,12 @@
|
|||
|
||||
#include "opensubdiv_capi.h"
|
||||
|
||||
bool BKE_subsurf_modifier_runtime_init(SubsurfModifierData *smd, const bool use_render_params)
|
||||
SubdivSettings BKE_subsurf_modifier_settings_init(const SubsurfModifierData *smd,
|
||||
const bool use_render_params)
|
||||
{
|
||||
const int requested_levels = (use_render_params) ? smd->renderLevels : smd->levels;
|
||||
|
||||
SubdivSettings settings;
|
||||
SubdivSettings settings{};
|
||||
settings.is_simple = (smd->subdivType == SUBSURF_TYPE_SIMPLE);
|
||||
settings.is_adaptive = !(smd->flags & eSubsurfModifierFlag_UseRecursiveSubdivision);
|
||||
settings.level = settings.is_simple ? 1 :
|
||||
|
@ -35,6 +36,13 @@ bool BKE_subsurf_modifier_runtime_init(SubsurfModifierData *smd, const bool use_
|
|||
settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
|
||||
smd->uv_smooth);
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
bool BKE_subsurf_modifier_runtime_init(SubsurfModifierData *smd, const bool use_render_params)
|
||||
{
|
||||
SubdivSettings settings = BKE_subsurf_modifier_settings_init(smd, use_render_params);
|
||||
|
||||
SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime;
|
||||
if (settings.level == 0) {
|
||||
/* Modifier is effectively disabled, but still update settings if runtime data
|
||||
|
|
|
@ -10,38 +10,34 @@
|
|||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_bounds_types.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
namespace blender::bounds {
|
||||
|
||||
template<typename T> struct MinMaxResult {
|
||||
T min;
|
||||
T max;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the smallest and largest values element-wise in the span.
|
||||
*/
|
||||
template<typename T> static std::optional<MinMaxResult<T>> min_max(Span<T> values)
|
||||
template<typename T> static std::optional<Bounds<T>> min_max(Span<T> values)
|
||||
{
|
||||
if (values.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const MinMaxResult<T> init{values.first(), values.first()};
|
||||
const Bounds<T> init{values.first(), values.first()};
|
||||
return threading::parallel_reduce(
|
||||
values.index_range(),
|
||||
1024,
|
||||
init,
|
||||
[&](IndexRange range, const MinMaxResult<T> &init) {
|
||||
MinMaxResult<T> result = init;
|
||||
[&](IndexRange range, const Bounds<T> &init) {
|
||||
Bounds<T> result = init;
|
||||
for (const int i : range) {
|
||||
math::min_max(values[i], result.min, result.max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const MinMaxResult<T> &a, const MinMaxResult<T> &b) {
|
||||
return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)};
|
||||
[](const Bounds<T> &a, const Bounds<T> &b) {
|
||||
return Bounds<T>{math::min(a.min, b.min), math::max(a.max, b.max)};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -50,27 +46,27 @@ template<typename T> static std::optional<MinMaxResult<T>> min_max(Span<T> value
|
|||
* first. The template type T is expected to have an addition operator implemented with RadiusT.
|
||||
*/
|
||||
template<typename T, typename RadiusT>
|
||||
static std::optional<MinMaxResult<T>> min_max_with_radii(Span<T> values, Span<RadiusT> radii)
|
||||
static std::optional<Bounds<T>> min_max_with_radii(Span<T> values, Span<RadiusT> radii)
|
||||
{
|
||||
BLI_assert(values.size() == radii.size());
|
||||
if (values.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const MinMaxResult<T> init{values.first(), values.first()};
|
||||
const Bounds<T> init{values.first(), values.first()};
|
||||
return threading::parallel_reduce(
|
||||
values.index_range(),
|
||||
1024,
|
||||
init,
|
||||
[&](IndexRange range, const MinMaxResult<T> &init) {
|
||||
MinMaxResult<T> result = init;
|
||||
[&](IndexRange range, const Bounds<T> &init) {
|
||||
Bounds<T> result = init;
|
||||
for (const int i : range) {
|
||||
result.min = math::min(values[i] - radii[i], result.min);
|
||||
result.max = math::max(values[i] + radii[i], result.max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const MinMaxResult<T> &a, const MinMaxResult<T> &b) {
|
||||
return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)};
|
||||
[](const Bounds<T> &a, const Bounds<T> &b) {
|
||||
return Bounds<T>{math::min(a.min, b.min), math::max(a.max, b.max)};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T> struct Bounds {
|
||||
T min;
|
||||
T max;
|
||||
};
|
||||
|
||||
} // namespace blender
|
|
@ -0,0 +1,69 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_cache_mutex.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* A `SharedCache` is meant to share lazily computed data between equivalent objects. It allows
|
||||
* saving unnecessary computation by making a calculated value accessible from any object that
|
||||
* shares the cache. Unlike `CacheMutex`, the cached data is embedded inside of this object.
|
||||
*
|
||||
* When data is copied (copy-on-write before changing a mesh, for example), the cache is shared,
|
||||
* allowing its calculation on either the source or original to make the result available on both
|
||||
* objects. As soon as either object is changed in a way that invalidates the cache, the data is
|
||||
* "un-shared", and they will no-longer influence each other.
|
||||
*
|
||||
* One important use case is a typical CoW update loop of a persistent geometry data-block in
|
||||
* `Main`. Even if bounds are only calculated on the evaluated *copied* geometry, if nothing
|
||||
* changes them, they only need to be calculated on the first evaluation, because the same
|
||||
* evaluated bounds are also accessible from the original geometry.
|
||||
*
|
||||
* The cache is implemented with a shared pointer, so it is relatively cheap, but to avoid
|
||||
* unnecessary overhead it should only be used for relatively expensive computations.
|
||||
*/
|
||||
template<typename T> class SharedCache {
|
||||
struct CacheData {
|
||||
CacheMutex mutex;
|
||||
T data;
|
||||
};
|
||||
std::shared_ptr<CacheData> cache_;
|
||||
|
||||
public:
|
||||
SharedCache()
|
||||
{
|
||||
/* The cache should be allocated to trigger sharing of the cached data as early as possible. */
|
||||
cache_ = std::make_shared<CacheData>();
|
||||
}
|
||||
|
||||
/** Tag the data for recomputation and stop sharing the cache with other objects. */
|
||||
void tag_dirty()
|
||||
{
|
||||
if (cache_.unique()) {
|
||||
cache_->mutex.tag_dirty();
|
||||
}
|
||||
else {
|
||||
cache_ = std::make_shared<CacheData>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the cache is dirty, trigger its computation with the provided function which should set
|
||||
* the proper data.
|
||||
*/
|
||||
void ensure(FunctionRef<void(T &data)> compute_cache)
|
||||
{
|
||||
cache_->mutex.ensure([&]() { compute_cache(this->cache_->data); });
|
||||
}
|
||||
|
||||
/** Retrieve the cached data. */
|
||||
const T &data()
|
||||
{
|
||||
BLI_assert(cache_->mutex.is_cached());
|
||||
return cache_->data;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
|
@ -177,6 +177,7 @@ set(SRC
|
|||
BLI_bitmap_draw_2d.h
|
||||
BLI_blenlib.h
|
||||
BLI_bounds.hh
|
||||
BLI_bounds_types.hh
|
||||
BLI_boxpack_2d.h
|
||||
BLI_buffer.h
|
||||
BLI_cache_mutex.hh
|
||||
|
@ -306,6 +307,7 @@ set(SRC
|
|||
BLI_session_uuid.h
|
||||
BLI_set.hh
|
||||
BLI_set_slots.hh
|
||||
BLI_shared_cache.hh
|
||||
BLI_simd.h
|
||||
BLI_smallhash.h
|
||||
BLI_sort.h
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "BKE_lib_override.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_main_namemap.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_screen.h"
|
||||
|
@ -3690,6 +3691,15 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 305, 1)) {
|
||||
/* Reset edge visibility flag, since the base is meant to be "true" for original meshes. */
|
||||
LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
|
||||
for (MEdge &edge : mesh->edges_for_write()) {
|
||||
edge.flag |= ME_EDGEDRAW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
|
|
@ -826,23 +826,6 @@ static void bm_to_mesh_shape(BMesh *bm,
|
|||
|
||||
/** \} */
|
||||
|
||||
BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
|
||||
{
|
||||
/* This is a cheap way to set the edge draw, its not precise and will
|
||||
* pick the first 2 faces an edge uses.
|
||||
* The dot comparison is a little arbitrary, but set so that a 5 subdivisions
|
||||
* ico-sphere won't vanish but 6 subdivisions will (as with pre-bmesh Blender). */
|
||||
|
||||
if (/* (med->flag & ME_EDGEDRAW) && */ /* Assume to be true. */
|
||||
(e->l && (e->l != e->l->radial_next)) &&
|
||||
(dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f)) {
|
||||
med->flag &= ~ME_EDGEDRAW;
|
||||
}
|
||||
else {
|
||||
med->flag |= ME_EDGEDRAW;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename GetFn>
|
||||
static void write_fn_to_attribute(blender::bke::MutableAttributeAccessor attributes,
|
||||
const StringRef attribute_name,
|
||||
|
@ -958,6 +941,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
|
|||
CustomData_free(&me->ldata, me->totloop);
|
||||
CustomData_free(&me->pdata, me->totpoly);
|
||||
|
||||
BKE_mesh_runtime_clear_geometry(me);
|
||||
|
||||
/* Add new custom data. */
|
||||
me->totvert = bm->totvert;
|
||||
me->totedge = bm->totedge;
|
||||
|
@ -994,10 +979,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
|
|||
bool need_hide_poly = false;
|
||||
bool need_material_index = false;
|
||||
|
||||
/* Clear normals on the mesh completely, since the original vertex and polygon count might be
|
||||
* different than the BMesh's. */
|
||||
BKE_mesh_clear_derived_normals(me);
|
||||
|
||||
i = 0;
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
copy_v3_v3(mvert[i].co, v->co);
|
||||
|
@ -1038,8 +1019,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
|
|||
/* Copy over custom-data. */
|
||||
CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i);
|
||||
|
||||
bmesh_quick_edgedraw_flag(&medge[i], e);
|
||||
|
||||
i++;
|
||||
BM_CHECK_ELEMENT(e);
|
||||
}
|
||||
|
@ -1205,9 +1184,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
|
|||
|
||||
/* Topology could be changed, ensure #CD_MDISPS are ok. */
|
||||
multires_topology_changed(me);
|
||||
|
||||
/* To be removed as soon as COW is enabled by default. */
|
||||
BKE_mesh_runtime_clear_geometry(me);
|
||||
}
|
||||
|
||||
/* NOTE: The function is called from multiple threads with the same input BMesh and different
|
||||
|
@ -1219,6 +1195,8 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
|
|||
/* Must be an empty mesh. */
|
||||
BLI_assert(me->totvert == 0);
|
||||
BLI_assert(cd_mask_extra == nullptr || (cd_mask_extra->vmask & CD_MASK_SHAPEKEY) == 0);
|
||||
/* Just in case, clear the derived geometry caches from the input mesh. */
|
||||
BKE_mesh_runtime_clear_geometry(me);
|
||||
|
||||
me->totvert = bm->totvert;
|
||||
me->totedge = bm->totedge;
|
||||
|
@ -1254,10 +1232,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
|
|||
MLoop *mloop = loops.data();
|
||||
uint i, j;
|
||||
|
||||
/* Clear normals on the mesh completely, since the original vertex and polygon count might be
|
||||
* different than the BMesh's. */
|
||||
BKE_mesh_clear_derived_normals(me);
|
||||
|
||||
me->runtime->deformed_only = true;
|
||||
|
||||
bke::MutableAttributeAccessor mesh_attributes = me->attributes_for_write();
|
||||
|
@ -1316,14 +1290,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
|
|||
select_edge_attribute.span[i] = true;
|
||||
}
|
||||
|
||||
/* Handle this differently to editmode switching,
|
||||
* only enable draw for single user edges rather than calculating angle. */
|
||||
if ((med->flag & ME_EDGEDRAW) == 0) {
|
||||
if (eed->l && eed->l == eed->l->radial_next) {
|
||||
med->flag |= ME_EDGEDRAW;
|
||||
}
|
||||
}
|
||||
|
||||
CustomData_from_bmesh_block(&bm->edata, &me->edata, eed->head.data, i);
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_EDGE;
|
||||
|
|
|
@ -2970,6 +2970,11 @@ void DepsgraphRelationBuilder::build_sound(bSound *sound)
|
|||
build_idproperties(sound->id.properties);
|
||||
build_animdata(&sound->id);
|
||||
build_parameters(&sound->id);
|
||||
|
||||
const ComponentKey parameters_key(&sound->id, NodeType::PARAMETERS);
|
||||
const ComponentKey audio_key(&sound->id, NodeType::AUDIO);
|
||||
|
||||
add_relation(parameters_key, audio_key, "Parameters -> Audio");
|
||||
}
|
||||
|
||||
void DepsgraphRelationBuilder::build_simulation(Simulation *simulation)
|
||||
|
|
|
@ -174,7 +174,7 @@ static void eevee_cache_finish(void *vedata)
|
|||
}
|
||||
|
||||
if (g_data->queued_shaders_count > 0) {
|
||||
SNPRINTF(ved->info, "Compiling Shaders %d", g_data->queued_shaders_count);
|
||||
SNPRINTF(ved->info, "Compiling Shaders (%d remaining)", g_data->queued_shaders_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ void EEVEE_shadows_cube_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *
|
|||
/* Saving light bounds for later. */
|
||||
BoundSphere *cube_bound = linfo->shadow_bounds + linfo->cube_len;
|
||||
copy_v3_v3(cube_bound->center, evli->position);
|
||||
cube_bound->radius = sqrt(1.0f / evli->invsqrdist);
|
||||
cube_bound->radius = sqrt(1.0f / min_ff(evli->invsqrdist, evli->invsqrdist_volume));
|
||||
|
||||
linfo->shadow_cube_light_indices[linfo->cube_len] = linfo->num_light;
|
||||
evli->shadow_id = linfo->shadow_len++;
|
||||
|
@ -87,7 +87,7 @@ bool EEVEE_shadows_cube_setup(EEVEE_LightsInfo *linfo, const EEVEE_Light *evli,
|
|||
|
||||
eevee_light_matrix_get(evli, cube_data->shadowmat);
|
||||
|
||||
shdw_data->far = max_ff(sqrt(1.0f / evli->invsqrdist), 3e-4);
|
||||
shdw_data->far = max_ff(sqrt(1.0f / min_ff(evli->invsqrdist, evli->invsqrdist_volume)), 3e-4);
|
||||
shdw_data->near = min_ff(shdw_data->near, shdw_data->far - 1e-4);
|
||||
|
||||
bool update = false;
|
||||
|
|
|
@ -20,7 +20,7 @@ layout(location = 1) out vec4 volumeExtinction;
|
|||
layout(location = 2) out vec4 volumeEmissive;
|
||||
layout(location = 3) out vec4 volumePhase;
|
||||
|
||||
int attr_id;
|
||||
int attr_id = 0;
|
||||
|
||||
#ifndef CLEAR
|
||||
GlobalData init_globals(void)
|
||||
|
|
|
@ -337,7 +337,7 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl)
|
|||
|
||||
if (materials.queued_shaders_count > 0) {
|
||||
std::stringstream ss;
|
||||
ss << "Compiling Shaders " << materials.queued_shaders_count;
|
||||
ss << "Compiling Shaders (" << materials.queued_shaders_count << " remaining)";
|
||||
info = ss.str();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,6 +139,8 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata)
|
|||
((is_paint_mode && do_tex_paint_shadows &&
|
||||
((draw_ctx->object_mode &
|
||||
(OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) ||
|
||||
(is_uv_editor && do_tex_paint_shadows &&
|
||||
((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)) ||
|
||||
(is_view_mode && do_tex_paint_shadows &&
|
||||
((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)) ||
|
||||
(do_uv_overlay && (show_modified_uvs)));
|
||||
|
|
|
@ -42,7 +42,7 @@ void DRW_curve_batch_cache_free(struct Curve *cu);
|
|||
|
||||
void DRW_mesh_batch_cache_dirty_tag(struct Mesh *me, eMeshBatchDirtyMode mode);
|
||||
void DRW_mesh_batch_cache_validate(struct Object *object, struct Mesh *me);
|
||||
void DRW_mesh_batch_cache_free(struct Mesh *me);
|
||||
void DRW_mesh_batch_cache_free(void *batch_cache);
|
||||
|
||||
void DRW_lattice_batch_cache_dirty_tag(struct Lattice *lt, int mode);
|
||||
void DRW_lattice_batch_cache_validate(struct Lattice *lt);
|
||||
|
|
|
@ -201,7 +201,7 @@ static constexpr DRWBatchFlag batches_that_use_buffer(const int buffer_index)
|
|||
}
|
||||
|
||||
static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache);
|
||||
static void mesh_batch_cache_clear(Mesh *me);
|
||||
static void mesh_batch_cache_clear(MeshBatchCache *cache);
|
||||
|
||||
static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatchFlag batch_map)
|
||||
{
|
||||
|
@ -618,7 +618,9 @@ static void mesh_batch_cache_init(Object *object, Mesh *me)
|
|||
void DRW_mesh_batch_cache_validate(Object *object, Mesh *me)
|
||||
{
|
||||
if (!mesh_batch_cache_valid(object, me)) {
|
||||
mesh_batch_cache_clear(me);
|
||||
if (me->runtime->batch_cache) {
|
||||
mesh_batch_cache_clear(static_cast<MeshBatchCache *>(me->runtime->batch_cache));
|
||||
}
|
||||
mesh_batch_cache_init(object, me);
|
||||
}
|
||||
}
|
||||
|
@ -819,12 +821,8 @@ static void mesh_batch_cache_free_subdiv_cache(MeshBatchCache *cache)
|
|||
}
|
||||
}
|
||||
|
||||
static void mesh_batch_cache_clear(Mesh *me)
|
||||
static void mesh_batch_cache_clear(MeshBatchCache *cache)
|
||||
{
|
||||
MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime->batch_cache);
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
|
||||
mesh_buffer_cache_clear(mbc);
|
||||
}
|
||||
|
@ -850,10 +848,12 @@ static void mesh_batch_cache_clear(Mesh *me)
|
|||
mesh_batch_cache_free_subdiv_cache(cache);
|
||||
}
|
||||
|
||||
void DRW_mesh_batch_cache_free(Mesh *me)
|
||||
void DRW_mesh_batch_cache_free(void *batch_cache)
|
||||
{
|
||||
mesh_batch_cache_clear(me);
|
||||
MEM_SAFE_FREE(me->runtime->batch_cache);
|
||||
if (batch_cache) {
|
||||
mesh_batch_cache_clear(static_cast<MeshBatchCache *>(batch_cache));
|
||||
MEM_freeN(batch_cache);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -56,6 +56,12 @@ void ResourceBind::execute() const
|
|||
case ResourceBind::Type::StorageBuf:
|
||||
GPU_storagebuf_bind(is_reference ? *storage_buf_ref : storage_buf, slot);
|
||||
break;
|
||||
case ResourceBind::Type::UniformAsStorageBuf:
|
||||
GPU_uniformbuf_bind_as_ssbo(is_reference ? *uniform_buf_ref : uniform_buf, slot);
|
||||
break;
|
||||
case ResourceBind::Type::VertexAsStorageBuf:
|
||||
GPU_vertbuf_bind_as_ssbo(is_reference ? *vertex_buf_ref : vertex_buf, slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,6 +272,12 @@ std::string ResourceBind::serialize() const
|
|||
case Type::StorageBuf:
|
||||
return std::string(".bind_storage_buf") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::UniformAsStorageBuf:
|
||||
return std::string(".bind_uniform_as_ssbo") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::VertexAsStorageBuf:
|
||||
return std::string(".bind_vertbuf_as_ssbo") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return "";
|
||||
|
|
|
@ -138,6 +138,8 @@ struct ResourceBind {
|
|||
Image,
|
||||
UniformBuf,
|
||||
StorageBuf,
|
||||
UniformAsStorageBuf,
|
||||
VertexAsStorageBuf,
|
||||
} type;
|
||||
|
||||
union {
|
||||
|
@ -164,6 +166,14 @@ struct ResourceBind {
|
|||
: slot(slot_), is_reference(false), type(Type::StorageBuf), storage_buf(res){};
|
||||
ResourceBind(int slot_, GPUStorageBuf **res)
|
||||
: slot(slot_), is_reference(true), type(Type::StorageBuf), storage_buf_ref(res){};
|
||||
ResourceBind(int slot_, GPUUniformBuf *res, Type /* type */)
|
||||
: slot(slot_), is_reference(false), type(Type::UniformAsStorageBuf), uniform_buf(res){};
|
||||
ResourceBind(int slot_, GPUUniformBuf **res, Type /* type */)
|
||||
: slot(slot_), is_reference(true), type(Type::UniformAsStorageBuf), uniform_buf_ref(res){};
|
||||
ResourceBind(int slot_, GPUVertBuf *res, Type /* type */)
|
||||
: slot(slot_), is_reference(false), type(Type::VertexAsStorageBuf), vertex_buf(res){};
|
||||
ResourceBind(int slot_, GPUVertBuf **res, Type /* type */)
|
||||
: slot(slot_), is_reference(true), type(Type::VertexAsStorageBuf), vertex_buf_ref(res){};
|
||||
ResourceBind(int slot_, draw::Image *res)
|
||||
: slot(slot_), is_reference(false), type(Type::Image), texture(draw::as_texture(res)){};
|
||||
ResourceBind(int slot_, draw::Image **res)
|
||||
|
|
|
@ -1333,10 +1333,6 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
|
|||
|
||||
const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d);
|
||||
|
||||
if (G.is_rendering && U.experimental.use_draw_manager_acquire_lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX Really nasty locking. But else this could
|
||||
* be executed by the material previews thread
|
||||
* while rendering a viewport. */
|
||||
|
|
|
@ -289,6 +289,14 @@ class PassBase {
|
|||
void bind_ssbo(const char *name, GPUStorageBuf **buffer);
|
||||
void bind_ssbo(int slot, GPUStorageBuf *buffer);
|
||||
void bind_ssbo(int slot, GPUStorageBuf **buffer);
|
||||
void bind_ssbo(const char *name, GPUUniformBuf *buffer);
|
||||
void bind_ssbo(const char *name, GPUUniformBuf **buffer);
|
||||
void bind_ssbo(int slot, GPUUniformBuf *buffer);
|
||||
void bind_ssbo(int slot, GPUUniformBuf **buffer);
|
||||
void bind_ssbo(const char *name, GPUVertBuf *buffer);
|
||||
void bind_ssbo(const char *name, GPUVertBuf **buffer);
|
||||
void bind_ssbo(int slot, GPUVertBuf *buffer);
|
||||
void bind_ssbo(int slot, GPUVertBuf **buffer);
|
||||
void bind_ubo(const char *name, GPUUniformBuf *buffer);
|
||||
void bind_ubo(const char *name, GPUUniformBuf **buffer);
|
||||
void bind_ubo(int slot, GPUUniformBuf *buffer);
|
||||
|
@ -841,6 +849,26 @@ template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUStorag
|
|||
this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer);
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUUniformBuf *buffer)
|
||||
{
|
||||
this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer);
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUUniformBuf **buffer)
|
||||
{
|
||||
this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer);
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUVertBuf *buffer)
|
||||
{
|
||||
this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer);
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUVertBuf **buffer)
|
||||
{
|
||||
this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer);
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ubo(const char *name, GPUUniformBuf *buffer)
|
||||
{
|
||||
this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer);
|
||||
|
@ -874,6 +902,30 @@ template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUStorageBuf *bu
|
|||
create_command(Type::ResourceBind).resource_bind = {slot, buffer};
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUUniformBuf *buffer)
|
||||
{
|
||||
create_command(Type::ResourceBind).resource_bind = {
|
||||
slot, buffer, ResourceBind::Type::UniformAsStorageBuf};
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUUniformBuf **buffer)
|
||||
{
|
||||
create_command(Type::ResourceBind).resource_bind = {
|
||||
slot, buffer, ResourceBind::Type::UniformAsStorageBuf};
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUVertBuf *buffer)
|
||||
{
|
||||
create_command(Type::ResourceBind).resource_bind = {
|
||||
slot, buffer, ResourceBind::Type::VertexAsStorageBuf};
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUVertBuf **buffer)
|
||||
{
|
||||
create_command(Type::ResourceBind).resource_bind = {
|
||||
slot, buffer, ResourceBind::Type::VertexAsStorageBuf};
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::bind_ubo(int slot, GPUUniformBuf *buffer)
|
||||
{
|
||||
create_command(Type::ResourceBind).resource_bind = {slot, buffer};
|
||||
|
|
|
@ -20,10 +20,10 @@ namespace blender::draw {
|
|||
* \{ */
|
||||
|
||||
struct MeshExtract_EdgeFac_Data {
|
||||
uchar *vbo_data;
|
||||
uint8_t *vbo_data;
|
||||
bool use_edge_render;
|
||||
/* Number of loop per edge. */
|
||||
uchar *edge_loop_count;
|
||||
uint8_t *edge_loop_count;
|
||||
};
|
||||
|
||||
static float loop_edge_factor_get(const float f_no[3],
|
||||
|
@ -59,8 +59,7 @@ static void extract_edge_fac_init(const MeshRenderData *mr,
|
|||
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(tls_data);
|
||||
|
||||
if (mr->extract_type == MR_EXTRACT_MESH) {
|
||||
data->edge_loop_count = static_cast<uchar *>(
|
||||
MEM_callocN(sizeof(uint32_t) * mr->edge_len, __func__));
|
||||
data->edge_loop_count = MEM_cnew_array<uint8_t>(mr->edge_len, __func__);
|
||||
|
||||
/* HACK(@fclem): Detecting the need for edge render.
|
||||
* We could have a flag in the mesh instead or check the modifier stack. */
|
||||
|
|
|
@ -2005,23 +2005,23 @@ static size_t animdata_filter_ds_cachefile(
|
|||
/* Helper for Mask Editing - mask layers */
|
||||
static size_t animdata_filter_mask_data(ListBase *anim_data, Mask *mask, const int filter_mode)
|
||||
{
|
||||
MaskLayer *masklay_act = BKE_mask_layer_active(mask);
|
||||
MaskLayer *masklay;
|
||||
const MaskLayer *masklay_act = BKE_mask_layer_active(mask);
|
||||
size_t items = 0;
|
||||
|
||||
/* loop over layers as the conditions are acceptable */
|
||||
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
|
||||
/* only if selected */
|
||||
if (ANIMCHANNEL_SELOK(SEL_MASKLAY(masklay))) {
|
||||
/* only if editable */
|
||||
if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_MASK(masklay)) {
|
||||
/* active... */
|
||||
if (!(filter_mode & ANIMFILTER_ACTIVE) || (masklay_act == masklay)) {
|
||||
/* add to list */
|
||||
ANIMCHANNEL_NEW_CHANNEL(masklay, ANIMTYPE_MASKLAYER, mask, NULL);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (MaskLayer *, masklay, &mask->masklayers) {
|
||||
if (!ANIMCHANNEL_SELOK(SEL_MASKLAY(masklay))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((filter_mode & ANIMFILTER_FOREDIT) && !EDITABLE_MASK(masklay)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((filter_mode & ANIMFILTER_ACTIVE) & (masklay_act != masklay)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ANIMCHANNEL_NEW_CHANNEL(masklay, ANIMTYPE_MASKLAYER, mask, NULL);
|
||||
}
|
||||
|
||||
return items;
|
||||
|
@ -2033,12 +2033,11 @@ static size_t animdata_filter_mask(Main *bmain,
|
|||
void *UNUSED(data),
|
||||
int filter_mode)
|
||||
{
|
||||
Mask *mask;
|
||||
size_t items = 0;
|
||||
|
||||
/* For now, grab mask data-blocks directly from main. */
|
||||
/* XXX: this is not good... */
|
||||
for (mask = bmain->masks.first; mask; mask = mask->id.next) {
|
||||
LISTBASE_FOREACH (Mask *, mask, &bmain->masks) {
|
||||
ListBase tmp_data = {NULL, NULL};
|
||||
size_t tmp_items = 0;
|
||||
|
||||
|
@ -2048,24 +2047,28 @@ static size_t animdata_filter_mask(Main *bmain,
|
|||
}
|
||||
|
||||
/* add mask animation channels */
|
||||
BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_MASK(mask)) {
|
||||
tmp_items += animdata_filter_mask_data(&tmp_data, mask, filter_mode);
|
||||
if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
|
||||
BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_MASK(mask)) {
|
||||
tmp_items += animdata_filter_mask_data(&tmp_data, mask, filter_mode);
|
||||
}
|
||||
END_ANIMFILTER_SUBCHANNELS;
|
||||
}
|
||||
END_ANIMFILTER_SUBCHANNELS;
|
||||
|
||||
/* did we find anything? */
|
||||
if (tmp_items) {
|
||||
/* include data-expand widget first */
|
||||
if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
|
||||
/* add mask data-block as channel too (if for drawing, and it has layers) */
|
||||
ANIMCHANNEL_NEW_CHANNEL(mask, ANIMTYPE_MASKDATABLOCK, NULL, NULL);
|
||||
}
|
||||
|
||||
/* now add the list of collected channels */
|
||||
BLI_movelisttolist(anim_data, &tmp_data);
|
||||
BLI_assert(BLI_listbase_is_empty(&tmp_data));
|
||||
items += tmp_items;
|
||||
if (!tmp_items) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* include data-expand widget first */
|
||||
if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
|
||||
/* add mask data-block as channel too (if for drawing, and it has layers) */
|
||||
ANIMCHANNEL_NEW_CHANNEL(mask, ANIMTYPE_MASKDATABLOCK, NULL, NULL);
|
||||
}
|
||||
|
||||
/* now add the list of collected channels */
|
||||
BLI_movelisttolist(anim_data, &tmp_data);
|
||||
BLI_assert(BLI_listbase_is_empty(&tmp_data));
|
||||
items += tmp_items;
|
||||
}
|
||||
|
||||
/* return the number of items added to the list */
|
||||
|
|
|
@ -421,7 +421,6 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
|
|||
* Even though this mesh wont typically have run-time data, the Python API can for e.g.
|
||||
* create loop-triangle cache here, which is confusing when left in the mesh, see: T90798. */
|
||||
BKE_mesh_runtime_clear_geometry(me);
|
||||
BKE_mesh_clear_derived_normals(me);
|
||||
|
||||
/* new material indices and material array */
|
||||
if (totmat) {
|
||||
|
@ -689,9 +688,6 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
|
|||
me->ldata = ldata;
|
||||
me->pdata = pdata;
|
||||
|
||||
/* Tag normals dirty because vertex positions could be changed from the original. */
|
||||
BKE_mesh_normals_tag_dirty(me);
|
||||
|
||||
/* old material array */
|
||||
for (a = 1; a <= ob->totcol; a++) {
|
||||
ma = ob->mat[a - 1];
|
||||
|
|
|
@ -157,11 +157,6 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
|
|||
new_mesh = mesh_fixed_poles;
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_VOLUME || mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK ||
|
||||
mesh->flag & ME_REMESH_REPROJECT_SCULPT_FACE_SETS) {
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) {
|
||||
BKE_shrinkwrap_remesh_target_project(new_mesh, mesh, ob);
|
||||
}
|
||||
|
@ -175,7 +170,6 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_VERTEX_COLORS) {
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
BKE_remesh_reproject_vertex_paint(new_mesh, mesh);
|
||||
}
|
||||
|
||||
|
@ -900,7 +894,6 @@ static void quadriflow_start_job(void *customdata, bool *stop, bool *do_update,
|
|||
}
|
||||
|
||||
if (qj->preserve_paint_mask) {
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
BKE_mesh_remesh_reproject_paint_mask(new_mesh, mesh);
|
||||
}
|
||||
|
||||
|
|
|
@ -268,35 +268,35 @@ eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b)
|
|||
return SCREEN_DIR_NONE;
|
||||
}
|
||||
|
||||
const vec2s *sa_bl = &sa_a->v1->vec;
|
||||
const vec2s *sa_tl = &sa_a->v2->vec;
|
||||
const vec2s *sa_tr = &sa_a->v3->vec;
|
||||
const vec2s *sa_br = &sa_a->v4->vec;
|
||||
short left_a = sa_a->v1->vec.x;
|
||||
short right_a = sa_a->v3->vec.x;
|
||||
short top_a = sa_a->v3->vec.y;
|
||||
short bottom_a = sa_a->v1->vec.y;
|
||||
|
||||
const vec2s *sb_bl = &sa_b->v1->vec;
|
||||
const vec2s *sb_tl = &sa_b->v2->vec;
|
||||
const vec2s *sb_tr = &sa_b->v3->vec;
|
||||
const vec2s *sb_br = &sa_b->v4->vec;
|
||||
short left_b = sa_b->v1->vec.x;
|
||||
short right_b = sa_b->v3->vec.x;
|
||||
short top_b = sa_b->v3->vec.y;
|
||||
short bottom_b = sa_b->v1->vec.y;
|
||||
|
||||
if (sa_bl->x == sb_br->x && sa_tl->x == sb_tr->x) { /* sa_a to right of sa_b = W */
|
||||
if ((MIN2(sa_tl->y, sb_tr->y) - MAX2(sa_bl->y, sb_br->y)) > AREAJOINTOLERANCEY) {
|
||||
return 0;
|
||||
}
|
||||
/* How much these areas share a common edge. */
|
||||
short overlapx = MIN2(right_a, right_b) - MAX2(left_a, left_b);
|
||||
short overlapy = MIN2(top_a, top_b) - MAX2(bottom_a, bottom_b);
|
||||
|
||||
/* Minimum overlap required. */
|
||||
const short minx = MIN3(AREAJOINTOLERANCEX, right_a - left_a, right_b - left_b);
|
||||
const short miny = MIN3(AREAJOINTOLERANCEY, top_a - bottom_a, top_b - bottom_b);
|
||||
|
||||
if (top_a == bottom_b && overlapx >= minx) {
|
||||
return 1; /* sa_a to bottom of sa_b = N */
|
||||
}
|
||||
else if (sa_tl->y == sb_bl->y && sa_tr->y == sb_br->y) { /* sa_a to bottom of sa_b = N */
|
||||
if ((MIN2(sa_tr->x, sb_br->x) - MAX2(sa_tl->x, sb_bl->x)) > AREAJOINTOLERANCEX) {
|
||||
return 1;
|
||||
}
|
||||
if (bottom_a == top_b && overlapx >= minx) {
|
||||
return 3; /* sa_a on top of sa_b = S */
|
||||
}
|
||||
else if (sa_tr->x == sb_tl->x && sa_br->x == sb_bl->x) { /* sa_a to left of sa_b = E */
|
||||
if ((MIN2(sa_tr->y, sb_tl->y) - MAX2(sa_br->y, sb_bl->y)) > AREAJOINTOLERANCEY) {
|
||||
return 2;
|
||||
}
|
||||
if (left_a == right_b && overlapy >= miny) {
|
||||
return 0; /* sa_a to right of sa_b = W */
|
||||
}
|
||||
else if (sa_bl->y == sb_tl->y && sa_br->y == sb_tr->y) { /* sa_a on top of sa_b = S */
|
||||
if ((MIN2(sa_br->x, sb_tr->x) - MAX2(sa_bl->x, sb_tl->x)) > AREAJOINTOLERANCEX) {
|
||||
return 3;
|
||||
}
|
||||
if (right_a == left_b && overlapy >= miny) {
|
||||
return 2; /* sa_a to left of sa_b = E */
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
@ -360,11 +360,65 @@ static void screen_verts_valign(const wmWindow *win,
|
|||
}
|
||||
}
|
||||
|
||||
/* Test if two adjoining areas can be aligned by having their screen edges adjusted. */
|
||||
static bool screen_areas_can_align(bScreen *screen, ScrArea *sa1, ScrArea *sa2, eScreenDir dir)
|
||||
{
|
||||
if (dir == SCREEN_DIR_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset1;
|
||||
int offset2;
|
||||
area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
|
||||
|
||||
const int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
|
||||
if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
|
||||
/* Misalignment is too great. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Areas that are _smaller_ than minimum sizes, sharing an edge to be moved. See T100772. */
|
||||
if (SCREEN_DIR_IS_VERTICAL(dir)) {
|
||||
const short xmin = MIN2(sa1->v1->vec.x, sa2->v1->vec.x);
|
||||
const short xmax = MAX2(sa1->v3->vec.x, sa2->v3->vec.x);
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
if (area == sa1 || area == sa2) {
|
||||
continue;
|
||||
}
|
||||
if (area->v3->vec.x - area->v1->vec.x < tolerance &&
|
||||
(area->v1->vec.x == xmin || area->v3->vec.x == xmax)) {
|
||||
/* There is a narrow vertical area sharing an edge of the combined bounds. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const short ymin = MIN2(sa1->v1->vec.y, sa2->v1->vec.y);
|
||||
const short ymax = MAX2(sa1->v3->vec.y, sa2->v3->vec.y);
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
if (area == sa1 || area == sa2) {
|
||||
continue;
|
||||
}
|
||||
if (area->v3->vec.y - area->v1->vec.y < tolerance &&
|
||||
(area->v1->vec.y == ymin || area->v3->vec.y == ymax)) {
|
||||
/* There is a narrow horizontal area sharing an edge of the combined bounds. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation().
|
||||
*/
|
||||
static void screen_areas_align(
|
||||
static bool screen_areas_align(
|
||||
bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const eScreenDir dir)
|
||||
{
|
||||
if (!screen_areas_can_align(screen, sa1, sa2, dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
if (SCREEN_DIR_IS_HORIZONTAL(dir)) {
|
||||
|
@ -393,28 +447,20 @@ static void screen_areas_align(
|
|||
screen_verts_halign(win, screen, sa2->v1->vec.x, left);
|
||||
screen_verts_halign(win, screen, sa2->v3->vec.x, right);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Simple join of two areas without any splitting. Will return false if not possible. */
|
||||
static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
|
||||
{
|
||||
const eScreenDir dir = area_getorientation(sa1, sa2);
|
||||
if (dir == SCREEN_DIR_NONE) {
|
||||
|
||||
/* Ensure that the area edges are exactly aligned. */
|
||||
if (!screen_areas_align(C, screen, sa1, sa2, dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset1;
|
||||
int offset2;
|
||||
area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
|
||||
|
||||
int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
|
||||
if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Align areas if they are not. */
|
||||
screen_areas_align(C, screen, sa1, sa2, dir);
|
||||
|
||||
if (dir == SCREEN_DIR_W) { /* sa1 to right of sa2 = West. */
|
||||
sa1->v1 = sa2->v1; /* BL */
|
||||
sa1->v2 = sa2->v2; /* TL */
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "BKE_report.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
@ -164,7 +166,8 @@ static int screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
}
|
||||
|
||||
/* extension is added by 'screenshot_check' after */
|
||||
char filepath[FILE_MAX] = "//screen";
|
||||
char filepath[FILE_MAX];
|
||||
BLI_snprintf(filepath, FILE_MAX, "//%s", DATA_("screen"));
|
||||
const char *blendfile_path = BKE_main_blendfile_path_from_global();
|
||||
if (blendfile_path[0] != '\0') {
|
||||
BLI_strncpy(filepath, blendfile_path, sizeof(filepath));
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "BLI_gsqueue.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
|
@ -69,6 +71,8 @@
|
|||
|
||||
#include "bmesh.h"
|
||||
|
||||
using blender::MutableSpan;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sculpt PBVH Abstraction API
|
||||
*
|
||||
|
@ -3682,48 +3686,46 @@ static void do_brush_action(Sculpt *sd,
|
|||
}
|
||||
|
||||
/* Flush displacement from deformed PBVH vertex to original mesh. */
|
||||
static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd)
|
||||
static void sculpt_flush_pbvhvert_deform(const SculptSession &ss,
|
||||
const PBVHVertexIter &vd,
|
||||
MutableSpan<MVert> verts)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Mesh *me = static_cast<Mesh *>(ob->data);
|
||||
float disp[3], newco[3];
|
||||
int index = vd->vert_indices[vd->i];
|
||||
int index = vd.vert_indices[vd.i];
|
||||
|
||||
sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]);
|
||||
mul_m3_v3(ss->deform_imats[index], disp);
|
||||
add_v3_v3v3(newco, disp, ss->orig_cos[index]);
|
||||
sub_v3_v3v3(disp, vd.co, ss.deform_cos[index]);
|
||||
mul_m3_v3(ss.deform_imats[index], disp);
|
||||
add_v3_v3v3(newco, disp, ss.orig_cos[index]);
|
||||
|
||||
copy_v3_v3(ss->deform_cos[index], vd->co);
|
||||
copy_v3_v3(ss->orig_cos[index], newco);
|
||||
copy_v3_v3(ss.deform_cos[index], vd.co);
|
||||
copy_v3_v3(ss.orig_cos[index], newco);
|
||||
|
||||
MVert *verts = BKE_mesh_verts_for_write(me);
|
||||
if (!ss->shapekey_active) {
|
||||
if (!ss.shapekey_active) {
|
||||
copy_v3_v3(verts[index].co, newco);
|
||||
}
|
||||
}
|
||||
|
||||
static void sculpt_combine_proxies_task_cb(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict /*tls*/)
|
||||
static void sculpt_combine_proxies_node(Object &object,
|
||||
Sculpt &sd,
|
||||
const bool use_orco,
|
||||
PBVHNode &node)
|
||||
{
|
||||
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
Sculpt *sd = data->sd;
|
||||
Object *ob = data->ob;
|
||||
const bool use_orco = data->use_proxies_orco;
|
||||
SculptSession *ss = object.sculpt;
|
||||
|
||||
PBVHVertexIter vd;
|
||||
PBVHProxyNode *proxies;
|
||||
int proxy_count;
|
||||
float(*orco)[3] = nullptr;
|
||||
|
||||
if (use_orco && !ss->bm) {
|
||||
orco = SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS)->co;
|
||||
orco = SCULPT_undo_push_node(&object, &node, SCULPT_UNDO_COORDS)->co;
|
||||
}
|
||||
|
||||
BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count);
|
||||
int proxy_count;
|
||||
PBVHProxyNode *proxies;
|
||||
BKE_pbvh_node_get_proxies(&node, &proxies, &proxy_count);
|
||||
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
Mesh &mesh = *static_cast<Mesh *>(object.data);
|
||||
MutableSpan<MVert> verts = mesh.verts_for_write();
|
||||
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, &node, vd, PBVH_ITER_UNIQUE) {
|
||||
float val[3];
|
||||
|
||||
if (use_orco) {
|
||||
|
@ -3742,23 +3744,22 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata,
|
|||
add_v3_v3(val, proxies[p].co[vd.i]);
|
||||
}
|
||||
|
||||
SCULPT_clip(sd, ss, vd.co, val);
|
||||
SCULPT_clip(&sd, ss, vd.co, val);
|
||||
|
||||
if (ss->deform_modifiers_active) {
|
||||
sculpt_flush_pbvhvert_deform(ob, &vd);
|
||||
sculpt_flush_pbvhvert_deform(*ss, vd, verts);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
|
||||
BKE_pbvh_node_free_proxies(data->nodes[n]);
|
||||
BKE_pbvh_node_free_proxies(&node);
|
||||
}
|
||||
|
||||
static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
|
||||
{
|
||||
using namespace blender;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
PBVHNode **nodes;
|
||||
int totnode;
|
||||
|
||||
if (!ss->cache->supports_gravity && sculpt_tool_is_proxy_used(brush->sculpt_tool)) {
|
||||
/* First line is tools that don't support proxies. */
|
||||
|
@ -3774,37 +3775,33 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
|
|||
SCULPT_TOOL_BOUNDARY,
|
||||
SCULPT_TOOL_POSE);
|
||||
|
||||
int totnode;
|
||||
PBVHNode **nodes;
|
||||
BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
|
||||
|
||||
SculptThreadedTaskData data{};
|
||||
data.sd = sd;
|
||||
data.ob = ob;
|
||||
data.brush = brush;
|
||||
data.nodes = nodes;
|
||||
data.use_proxies_orco = use_orco;
|
||||
threading::parallel_for(IndexRange(totnode), 1, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
sculpt_combine_proxies_node(*ob, *sd, use_orco, *nodes[i]);
|
||||
}
|
||||
});
|
||||
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings);
|
||||
MEM_SAFE_FREE(nodes);
|
||||
}
|
||||
|
||||
void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob)
|
||||
{
|
||||
using namespace blender;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
PBVHNode **nodes;
|
||||
|
||||
int totnode;
|
||||
|
||||
PBVHNode **nodes;
|
||||
BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
|
||||
SculptThreadedTaskData data{};
|
||||
data.sd = sd;
|
||||
data.ob = ob;
|
||||
data.nodes = nodes;
|
||||
data.use_proxies_orco = false;
|
||||
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings);
|
||||
threading::parallel_for(IndexRange(totnode), 1, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
sculpt_combine_proxies_node(*ob, *sd, false, *nodes[i]);
|
||||
}
|
||||
});
|
||||
|
||||
MEM_SAFE_FREE(nodes);
|
||||
}
|
||||
|
@ -3837,34 +3834,10 @@ static void sculpt_update_keyblock(Object *ob)
|
|||
}
|
||||
}
|
||||
|
||||
static void SCULPT_flush_stroke_deform_task_cb(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict /*tls*/)
|
||||
{
|
||||
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
Object *ob = data->ob;
|
||||
float(*vertCos)[3] = data->vertCos;
|
||||
|
||||
PBVHVertexIter vd;
|
||||
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
sculpt_flush_pbvhvert_deform(ob, &vd);
|
||||
|
||||
if (!vertCos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = vd.vert_indices[vd.i];
|
||||
copy_v3_v3(vertCos[index], ss->orig_cos[index]);
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
|
||||
void SCULPT_flush_stroke_deform(Sculpt * /*sd*/, Object *ob, bool is_proxy_used)
|
||||
{
|
||||
using namespace blender;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
|
||||
if (is_proxy_used && ss->deform_modifiers_active) {
|
||||
/* This brushes aren't using proxies, so sculpt_combine_proxies() wouldn't propagate needed
|
||||
|
@ -3886,16 +3859,24 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
|
|||
|
||||
BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode);
|
||||
|
||||
SculptThreadedTaskData data{};
|
||||
data.sd = sd;
|
||||
data.ob = ob;
|
||||
data.brush = brush;
|
||||
data.nodes = nodes;
|
||||
data.vertCos = vertCos;
|
||||
MutableSpan<MVert> verts = me->verts_for_write();
|
||||
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
BLI_task_parallel_range(0, totnode, &data, SCULPT_flush_stroke_deform_task_cb, &settings);
|
||||
threading::parallel_for(IndexRange(totnode), 1, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[i], vd, PBVH_ITER_UNIQUE) {
|
||||
sculpt_flush_pbvhvert_deform(*ss, vd, verts);
|
||||
|
||||
if (!vertCos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = vd.vert_indices[vd.i];
|
||||
copy_v3_v3(vertCos[index], ss->orig_cos[index]);
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
});
|
||||
|
||||
if (vertCos) {
|
||||
SCULPT_vertcos_to_key(ob, ss->shapekey_active, vertCos);
|
||||
|
@ -5300,12 +5281,14 @@ void SCULPT_update_object_bounding_box(Object *ob)
|
|||
|
||||
void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags)
|
||||
{
|
||||
using namespace blender;
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
MultiresModifierData *mmd = ss->multires.modifier;
|
||||
RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
|
||||
if (rv3d) {
|
||||
/* Mark for faster 3D viewport redraws. */
|
||||
|
@ -5364,6 +5347,17 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags)
|
|||
ED_region_tag_redraw_partial(region, &r, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (update_flags & SCULPT_UPDATE_COORDS && !ss->shapekey_active) {
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
|
||||
/* When sculpting and changing the positions of a mesh, tag them as changed and update. */
|
||||
BKE_mesh_tag_coords_changed(mesh);
|
||||
/* Update the mesh's bounds eagerly since the PBVH already has that information. */
|
||||
mesh->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
|
||||
BKE_pbvh_bounding_box(ob->sculpt->pbvh, r_bounds.min, r_bounds.max);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags)
|
||||
|
|
|
@ -220,7 +220,6 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
|
|||
BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist);
|
||||
|
||||
ED_sculpt_undo_geometry_end(ob);
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
||||
break;
|
||||
|
|
|
@ -503,6 +503,11 @@ void ED_node_shader_default(const bContext *C, ID *id)
|
|||
|
||||
ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree);
|
||||
ma->nodetree->owner_id = &ma->id;
|
||||
LISTBASE_FOREACH (bNode *, node_iter, &ma->nodetree->nodes) {
|
||||
BLI_strncpy(node_iter->name, DATA_(node_iter->name), NODE_MAXSTR);
|
||||
nodeUniqueName(ma->nodetree, node_iter);
|
||||
}
|
||||
|
||||
BKE_ntree_update_main_tree(bmain, ma->nodetree, nullptr);
|
||||
}
|
||||
else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
|
||||
|
@ -2237,7 +2242,6 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator * /*op*/)
|
|||
|
||||
/* clear current clipboard */
|
||||
BKE_node_clipboard_clear();
|
||||
BKE_node_clipboard_init(ntree);
|
||||
|
||||
Map<const bNode *, bNode *> node_map;
|
||||
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
||||
|
@ -2326,11 +2330,6 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (BKE_node_clipboard_get_type() != ntree->type) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Clipboard nodes are an incompatible type");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* only warn */
|
||||
if (is_clipboard_valid == false) {
|
||||
BKE_report(op->reports,
|
||||
|
@ -2338,34 +2337,6 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||
"Some nodes references could not be restored, will be left empty");
|
||||
}
|
||||
|
||||
/* make sure all clipboard nodes would be valid in the target tree */
|
||||
bool all_nodes_valid = true;
|
||||
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
|
||||
const char *disabled_hint = nullptr;
|
||||
if (!node->typeinfo->poll_instance ||
|
||||
!node->typeinfo->poll_instance(node, ntree, &disabled_hint)) {
|
||||
all_nodes_valid = false;
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Cannot add node %s into node tree %s:\n %s",
|
||||
node->name,
|
||||
ntree->id.name + 2,
|
||||
disabled_hint);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Cannot add node %s into node tree %s",
|
||||
node->name,
|
||||
ntree->id.name + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!all_nodes_valid) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
||||
|
||||
/* deselect old nodes */
|
||||
|
@ -2383,11 +2354,32 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||
Map<const bNode *, bNode *> node_map;
|
||||
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
||||
|
||||
/* copy nodes from clipboard */
|
||||
/* copy valid nodes from clipboard */
|
||||
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
|
||||
bNode *new_node = bke::node_copy_with_mapping(
|
||||
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
|
||||
node_map.add_new(node, new_node);
|
||||
const char *disabled_hint = nullptr;
|
||||
if (node->typeinfo->poll_instance &&
|
||||
node->typeinfo->poll_instance(node, ntree, &disabled_hint)) {
|
||||
bNode *new_node = bke::node_copy_with_mapping(
|
||||
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
|
||||
node_map.add_new(node, new_node);
|
||||
}
|
||||
else {
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Cannot add node %s into node tree %s: %s",
|
||||
node->name,
|
||||
ntree->id.name + 2,
|
||||
disabled_hint);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Cannot add node %s into node tree %s",
|
||||
node->name,
|
||||
ntree->id.name + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (bNode *new_node : node_map.values()) {
|
||||
|
@ -2402,15 +2394,19 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
}
|
||||
|
||||
/* Add links between existing nodes. */
|
||||
LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) {
|
||||
bNodeLink *new_link = nodeAddLink(ntree,
|
||||
node_map.lookup(link->fromnode),
|
||||
socket_map.lookup(link->fromsock),
|
||||
node_map.lookup(link->tonode),
|
||||
socket_map.lookup(link->tosock));
|
||||
new_link->multi_input_socket_index = link->multi_input_socket_index;
|
||||
const bNode *fromnode = link->fromnode;
|
||||
const bNode *tonode = link->tonode;
|
||||
if (node_map.lookup_key_ptr(fromnode) && node_map.lookup_key_ptr(tonode)) {
|
||||
bNodeLink *new_link = nodeAddLink(ntree,
|
||||
node_map.lookup(fromnode),
|
||||
socket_map.lookup(link->fromsock),
|
||||
node_map.lookup(tonode),
|
||||
socket_map.lookup(link->tosock));
|
||||
new_link->multi_input_socket_index = link->multi_input_socket_index;
|
||||
}
|
||||
}
|
||||
|
||||
ntree->ensure_topology_cache();
|
||||
|
||||
for (bNode *new_node : node_map.values()) {
|
||||
|
|
|
@ -197,10 +197,14 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
|
|||
/* Relink all node links to the newly active output socket. */
|
||||
bNodeSocket *output_socket = bke::node_find_enabled_output_socket(*node, "Attribute");
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &node_tree->links) {
|
||||
if (link->fromnode == node) {
|
||||
link->fromsock = output_socket;
|
||||
BKE_ntree_update_tag_link_changed(node_tree);
|
||||
if (link->fromnode != node) {
|
||||
continue;
|
||||
}
|
||||
if (!STREQ(link->fromsock->name, "Attribute")) {
|
||||
continue;
|
||||
}
|
||||
link->fromsock = output_socket;
|
||||
BKE_ntree_update_tag_link_changed(node_tree);
|
||||
}
|
||||
}
|
||||
BKE_ntree_update_tag_node_property(node_tree, node);
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_editmesh.h"
|
||||
|
@ -42,7 +41,9 @@
|
|||
#include "BKE_mesh.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_subsurf.h"
|
||||
#include "BKE_subdiv.h"
|
||||
#include "BKE_subdiv_mesh.h"
|
||||
#include "BKE_subdiv_modifier.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
|
@ -569,6 +570,29 @@ static void texface_from_original_index(const Scene *scene,
|
|||
}
|
||||
}
|
||||
|
||||
static Mesh *subdivide_edit_mesh(const Object *object,
|
||||
const BMEditMesh *em,
|
||||
const SubsurfModifierData *smd)
|
||||
{
|
||||
Mesh *me_from_em = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, object->data);
|
||||
BKE_mesh_ensure_default_orig_index_customdata(me_from_em);
|
||||
|
||||
SubdivSettings settings = BKE_subsurf_modifier_settings_init(smd, false);
|
||||
if (settings.level == 1) {
|
||||
return me_from_em;
|
||||
}
|
||||
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
mesh_settings.resolution = (1 << smd->levels) + 1;
|
||||
mesh_settings.use_optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges);
|
||||
|
||||
Subdiv *subdiv = BKE_subdiv_update_from_mesh(NULL, &settings, me_from_em);
|
||||
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me_from_em);
|
||||
BKE_id_free(NULL, me_from_em);
|
||||
BKE_subdiv_free(subdiv);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap handle initialization for subsurf aware-unwrapper.
|
||||
* The many modifications required to make the original function(see above)
|
||||
|
@ -580,28 +604,11 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
|
|||
const UnwrapOptions *options,
|
||||
UnwrapResultInfo *result_info)
|
||||
{
|
||||
/* index pointers */
|
||||
MPoly *mpoly;
|
||||
MLoop *mloop;
|
||||
MEdge *edge;
|
||||
int i;
|
||||
|
||||
/* pointers to modifier data for unwrap control */
|
||||
ModifierData *md;
|
||||
SubsurfModifierData *smd_real;
|
||||
/* Modifier initialization data, will control what type of subdivision will happen. */
|
||||
SubsurfModifierData smd = {{NULL}};
|
||||
/* Used to hold subsurfed Mesh */
|
||||
DerivedMesh *derivedMesh, *initialDerived;
|
||||
/* holds original indices for subsurfed mesh */
|
||||
const int *origVertIndices, *origEdgeIndices, *origPolyIndices;
|
||||
/* Holds vertices of subsurfed mesh */
|
||||
MVert *subsurfedVerts;
|
||||
MEdge *subsurfedEdges;
|
||||
MPoly *subsurfedPolys;
|
||||
MLoop *subsurfedLoops;
|
||||
/* Number of vertices and faces for subsurfed mesh. */
|
||||
int numOfEdges, numOfFaces;
|
||||
|
||||
/* holds a map to editfaces for every subsurfed MFace.
|
||||
* These will be used to get hidden/ selected flags etc. */
|
||||
|
@ -629,44 +636,34 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
|
|||
|
||||
smd.levels = smd_real->levels;
|
||||
smd.subdivType = smd_real->subdivType;
|
||||
smd.flags = smd_real->flags;
|
||||
smd.quality = smd_real->quality;
|
||||
|
||||
{
|
||||
Mesh *me_from_em = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, ob->data);
|
||||
initialDerived = CDDM_from_mesh(me_from_em);
|
||||
derivedMesh = subsurf_make_derived_from_derived(
|
||||
initialDerived, &smd, scene, NULL, SUBSURF_IN_EDIT_MODE);
|
||||
Mesh *subdiv_mesh = subdivide_edit_mesh(ob, em, &smd);
|
||||
|
||||
initialDerived->release(initialDerived);
|
||||
BKE_id_free(NULL, me_from_em);
|
||||
}
|
||||
const MVert *subsurfedVerts = BKE_mesh_verts(subdiv_mesh);
|
||||
const MEdge *subsurfedEdges = BKE_mesh_edges(subdiv_mesh);
|
||||
const MPoly *subsurfedPolys = BKE_mesh_polys(subdiv_mesh);
|
||||
const MLoop *subsurfedLoops = BKE_mesh_loops(subdiv_mesh);
|
||||
|
||||
/* get the derived data */
|
||||
subsurfedVerts = derivedMesh->getVertArray(derivedMesh);
|
||||
subsurfedEdges = derivedMesh->getEdgeArray(derivedMesh);
|
||||
subsurfedPolys = derivedMesh->getPolyArray(derivedMesh);
|
||||
subsurfedLoops = derivedMesh->getLoopArray(derivedMesh);
|
||||
const int *origVertIndices = CustomData_get_layer(&subdiv_mesh->vdata, CD_ORIGINDEX);
|
||||
const int *origEdgeIndices = CustomData_get_layer(&subdiv_mesh->edata, CD_ORIGINDEX);
|
||||
const int *origPolyIndices = CustomData_get_layer(&subdiv_mesh->pdata, CD_ORIGINDEX);
|
||||
|
||||
origVertIndices = derivedMesh->getVertDataArray(derivedMesh, CD_ORIGINDEX);
|
||||
origEdgeIndices = derivedMesh->getEdgeDataArray(derivedMesh, CD_ORIGINDEX);
|
||||
origPolyIndices = derivedMesh->getPolyDataArray(derivedMesh, CD_ORIGINDEX);
|
||||
|
||||
numOfEdges = derivedMesh->getNumEdges(derivedMesh);
|
||||
numOfFaces = derivedMesh->getNumPolys(derivedMesh);
|
||||
|
||||
faceMap = MEM_mallocN(numOfFaces * sizeof(BMFace *), "unwrap_edit_face_map");
|
||||
faceMap = MEM_mallocN(subdiv_mesh->totpoly * sizeof(BMFace *), "unwrap_edit_face_map");
|
||||
|
||||
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
|
||||
BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_FACE);
|
||||
|
||||
/* map subsurfed faces to original editFaces */
|
||||
for (i = 0; i < numOfFaces; i++) {
|
||||
for (int i = 0; i < subdiv_mesh->totpoly; i++) {
|
||||
faceMap[i] = BM_face_at_index(em->bm, origPolyIndices[i]);
|
||||
}
|
||||
|
||||
edgeMap = MEM_mallocN(numOfEdges * sizeof(BMEdge *), "unwrap_edit_edge_map");
|
||||
edgeMap = MEM_mallocN(subdiv_mesh->totedge * sizeof(BMEdge *), "unwrap_edit_edge_map");
|
||||
|
||||
/* map subsurfed edges to original editEdges */
|
||||
for (i = 0; i < numOfEdges; i++) {
|
||||
for (int i = 0; i < subdiv_mesh->totedge; i++) {
|
||||
/* not all edges correspond to an old edge */
|
||||
edgeMap[i] = (origEdgeIndices[i] != ORIGINDEX_NONE) ?
|
||||
BM_edge_at_index(em->bm, origEdgeIndices[i]) :
|
||||
|
@ -674,7 +671,8 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
|
|||
}
|
||||
|
||||
/* Prepare and feed faces to the solver */
|
||||
for (i = 0, mpoly = subsurfedPolys; i < numOfFaces; i++, mpoly++) {
|
||||
for (int i = 0; i < subdiv_mesh->totpoly; i++) {
|
||||
const MPoly *mpoly = &subsurfedPolys[i];
|
||||
ParamKey key, vkeys[4];
|
||||
bool pin[4], select[4];
|
||||
const float *co[4];
|
||||
|
@ -693,7 +691,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
|
|||
}
|
||||
}
|
||||
|
||||
mloop = &subsurfedLoops[mpoly->loopstart];
|
||||
const MLoop *mloop = &subsurfedLoops[mpoly->loopstart];
|
||||
|
||||
/* We will not check for v4 here. Sub-surface faces always have 4 vertices. */
|
||||
BLI_assert(mpoly->totloop == 4);
|
||||
|
@ -744,8 +742,9 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
|
|||
}
|
||||
|
||||
/* these are calculated from original mesh too */
|
||||
for (edge = subsurfedEdges, i = 0; i < numOfEdges; i++, edge++) {
|
||||
for (int i = 0; i < subdiv_mesh->totedge; i++) {
|
||||
if ((edgeMap[i] != NULL) && BM_elem_flag_test(edgeMap[i], BM_ELEM_SEAM)) {
|
||||
const MEdge *edge = &subsurfedEdges[i];
|
||||
ParamKey vkeys[2];
|
||||
vkeys[0] = (ParamKey)edge->v1;
|
||||
vkeys[1] = (ParamKey)edge->v2;
|
||||
|
@ -761,7 +760,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
|
|||
/* cleanup */
|
||||
MEM_freeN(faceMap);
|
||||
MEM_freeN(edgeMap);
|
||||
derivedMesh->release(derivedMesh);
|
||||
BKE_id_free(NULL, subdiv_mesh);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
|
|
@ -797,7 +797,6 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex)
|
|||
} // loop over strokes
|
||||
|
||||
BKE_object_materials_test(freestyle_bmain, object_mesh, (ID *)mesh);
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
|
||||
#if 0 // XXX
|
||||
BLI_assert(mesh->totvert == vertex_index);
|
||||
|
|
|
@ -153,7 +153,7 @@ struct NodeState {
|
|||
/**
|
||||
* Set to true once the node is done running for the first time.
|
||||
*/
|
||||
bool had_initialization = true;
|
||||
bool had_initialization = false;
|
||||
/**
|
||||
* Nodes with side effects should always be executed when their required inputs have been
|
||||
* computed.
|
||||
|
@ -306,7 +306,20 @@ class Executor {
|
|||
|
||||
this->set_always_unused_graph_inputs();
|
||||
this->set_defaulted_graph_outputs();
|
||||
this->schedule_side_effect_nodes(current_task);
|
||||
|
||||
/* Retrieve and tag side effect nodes. */
|
||||
Vector<const FunctionNode *> side_effect_nodes;
|
||||
if (self_.side_effect_provider_ != nullptr) {
|
||||
side_effect_nodes = self_.side_effect_provider_->get_nodes_with_side_effects(*context_);
|
||||
for (const FunctionNode *node : side_effect_nodes) {
|
||||
const int node_index = node->index_in_graph();
|
||||
NodeState &node_state = *node_states_[node_index];
|
||||
node_state.has_side_effects = true;
|
||||
}
|
||||
}
|
||||
|
||||
this->initialize_static_value_usages(side_effect_nodes);
|
||||
this->schedule_side_effect_nodes(side_effect_nodes, current_task);
|
||||
}
|
||||
|
||||
this->schedule_newly_requested_outputs(current_task);
|
||||
|
@ -346,15 +359,6 @@ class Executor {
|
|||
|
||||
node_state.inputs = allocator.construct_array<InputState>(node_inputs.size());
|
||||
node_state.outputs = allocator.construct_array<OutputState>(node_outputs.size());
|
||||
|
||||
for (const int i : node_outputs.index_range()) {
|
||||
OutputState &output_state = node_state.outputs[i];
|
||||
const OutputSocket &output_socket = *node_outputs[i];
|
||||
output_state.potential_target_sockets = output_socket.targets().size();
|
||||
if (output_state.potential_target_sockets == 0) {
|
||||
output_state.usage = ValueUsage::Unused;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void destruct_node_state(const Node &node, NodeState &node_state)
|
||||
|
@ -425,19 +429,95 @@ class Executor {
|
|||
}
|
||||
}
|
||||
|
||||
void schedule_side_effect_nodes(CurrentTask ¤t_task)
|
||||
/**
|
||||
* Determines which nodes might be executed and which are unreachable. The set of reachable nodes
|
||||
* can dynamically depend on the side effect nodes.
|
||||
*
|
||||
* Most importantly, this function initializes `InputState.usage` and
|
||||
* `OutputState.potential_target_sockets`.
|
||||
*/
|
||||
void initialize_static_value_usages(const Span<const FunctionNode *> side_effect_nodes)
|
||||
{
|
||||
if (self_.side_effect_provider_ != nullptr) {
|
||||
const Vector<const FunctionNode *> side_effect_nodes =
|
||||
self_.side_effect_provider_->get_nodes_with_side_effects(*context_);
|
||||
for (const FunctionNode *node : side_effect_nodes) {
|
||||
NodeState &node_state = *node_states_[node->index_in_graph()];
|
||||
node_state.has_side_effects = true;
|
||||
this->with_locked_node(*node, node_state, current_task, [&](LockedNode &locked_node) {
|
||||
this->schedule_node(locked_node, current_task);
|
||||
});
|
||||
const Span<const Node *> all_nodes = self_.graph_.nodes();
|
||||
|
||||
/* Used for a search through all nodes that outputs depend on. */
|
||||
Stack<const Node *> reachable_nodes_to_check;
|
||||
Array<bool> reachable_node_flags(all_nodes.size(), false);
|
||||
|
||||
/* Graph outputs are always reachable. */
|
||||
for (const InputSocket *socket : self_.graph_outputs_) {
|
||||
const Node &node = socket->node();
|
||||
const int node_index = node.index_in_graph();
|
||||
if (!reachable_node_flags[node_index]) {
|
||||
reachable_node_flags[node_index] = true;
|
||||
reachable_nodes_to_check.push(&node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Side effect nodes are always reachable. */
|
||||
for (const FunctionNode *node : side_effect_nodes) {
|
||||
const int node_index = node->index_in_graph();
|
||||
reachable_node_flags[node_index] = true;
|
||||
reachable_nodes_to_check.push(node);
|
||||
}
|
||||
|
||||
/* Tag every node that reachable nodes depend on using depth-first-search. */
|
||||
while (!reachable_nodes_to_check.is_empty()) {
|
||||
const Node &node = *reachable_nodes_to_check.pop();
|
||||
for (const InputSocket *input_socket : node.inputs()) {
|
||||
const OutputSocket *origin_socket = input_socket->origin();
|
||||
if (origin_socket != nullptr) {
|
||||
const Node &origin_node = origin_socket->node();
|
||||
const int origin_node_index = origin_node.index_in_graph();
|
||||
if (!reachable_node_flags[origin_node_index]) {
|
||||
reachable_node_flags[origin_node_index] = true;
|
||||
reachable_nodes_to_check.push(&origin_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const int node_index : reachable_node_flags.index_range()) {
|
||||
const Node &node = *all_nodes[node_index];
|
||||
NodeState &node_state = *node_states_[node_index];
|
||||
const bool node_is_reachable = reachable_node_flags[node_index];
|
||||
if (node_is_reachable) {
|
||||
for (const int output_index : node.outputs().index_range()) {
|
||||
const OutputSocket &output_socket = node.output(output_index);
|
||||
OutputState &output_state = node_state.outputs[output_index];
|
||||
int use_count = 0;
|
||||
for (const InputSocket *target_socket : output_socket.targets()) {
|
||||
const Node &target_node = target_socket->node();
|
||||
const bool target_is_reachable = reachable_node_flags[target_node.index_in_graph()];
|
||||
/* Only count targets that are reachable. */
|
||||
if (target_is_reachable) {
|
||||
use_count++;
|
||||
}
|
||||
}
|
||||
output_state.potential_target_sockets = use_count;
|
||||
if (use_count == 0) {
|
||||
output_state.usage = ValueUsage::Unused;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Inputs of unreachable nodes are unused. */
|
||||
for (InputState &input_state : node_state.inputs) {
|
||||
input_state.usage = ValueUsage::Unused;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void schedule_side_effect_nodes(const Span<const FunctionNode *> side_effect_nodes,
|
||||
CurrentTask ¤t_task)
|
||||
{
|
||||
for (const FunctionNode *node : side_effect_nodes) {
|
||||
NodeState &node_state = *node_states_[node->index_in_graph()];
|
||||
this->with_locked_node(*node, node_state, current_task, [&](LockedNode &locked_node) {
|
||||
this->schedule_node(locked_node, current_task);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void forward_newly_provided_inputs(CurrentTask ¤t_task)
|
||||
|
@ -638,7 +718,7 @@ class Executor {
|
|||
return;
|
||||
}
|
||||
|
||||
if (node_state.had_initialization) {
|
||||
if (!node_state.had_initialization) {
|
||||
/* Initialize storage. */
|
||||
node_state.storage = fn.init_storage(allocator);
|
||||
|
||||
|
@ -670,7 +750,7 @@ class Executor {
|
|||
}
|
||||
}
|
||||
|
||||
node_state.had_initialization = false;
|
||||
node_state.had_initialization = true;
|
||||
}
|
||||
|
||||
for (const int input_index : node_state.inputs.index_range()) {
|
||||
|
|
|
@ -37,16 +37,21 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
|
|||
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
|
||||
|
||||
MutableSpan<int> point_offsets = dst_point_offsets.slice(src_segments);
|
||||
|
||||
MutableSpan<int> point_counts = point_offsets.drop_back(1);
|
||||
cuts.materialize_compressed(src_points, point_counts);
|
||||
for (int &count : point_counts) {
|
||||
/* Make sure the number of cuts is greater than zero and add one for the existing point. */
|
||||
count = std::max(count, 0) + 1;
|
||||
|
||||
if (src_points.size() == 1) {
|
||||
point_counts.first() = 1;
|
||||
}
|
||||
if (!cyclic[curve_i]) {
|
||||
/* The last point only has a segment to be subdivided if the curve isn't cyclic. */
|
||||
point_counts.last() = 1;
|
||||
else {
|
||||
cuts.materialize_compressed(src_points, point_counts);
|
||||
for (int &count : point_counts) {
|
||||
/* Make sure there at least one cut, and add one for the existing point. */
|
||||
count = std::max(count, 0) + 1;
|
||||
}
|
||||
if (!cyclic[curve_i]) {
|
||||
/* The last point only has a segment to be subdivided if the curve isn't cyclic. */
|
||||
point_counts.last() = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bke::curves::accumulate_counts_to_offsets(point_offsets);
|
||||
|
|
|
@ -3927,9 +3927,8 @@ void GEO_uv_parametrizer_face_add(ParamHandle *phandle,
|
|||
const bool *pin,
|
||||
const bool *select)
|
||||
{
|
||||
param_assert(phash_lookup(phandle->hash_faces, key) == nullptr);
|
||||
BLI_assert(nverts >= 3);
|
||||
param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
|
||||
param_assert(ELEM(nverts, 3, 4));
|
||||
|
||||
if (nverts > 4) {
|
||||
/* ngon */
|
||||
|
|
|
@ -43,6 +43,8 @@ void GPU_uniformbuf_bind_as_ssbo(GPUUniformBuf *ubo, int slot);
|
|||
void GPU_uniformbuf_unbind(GPUUniformBuf *ubo);
|
||||
void GPU_uniformbuf_unbind_all(void);
|
||||
|
||||
void GPU_uniformbuf_clear_to_zero(GPUUniformBuf *ubo);
|
||||
|
||||
#define GPU_UBO_BLOCK_NAME "node_tree"
|
||||
#define GPU_ATTRIBUTE_UBO_BLOCK_NAME "unf_attrs"
|
||||
#define GPU_LAYER_ATTRIBUTE_UBO_BLOCK_NAME "drw_layer_attrs"
|
||||
|
|
|
@ -233,4 +233,9 @@ void GPU_uniformbuf_unbind_all()
|
|||
/* FIXME */
|
||||
}
|
||||
|
||||
void GPU_uniformbuf_clear_to_zero(GPUUniformBuf *ubo)
|
||||
{
|
||||
unwrap(ubo)->clear_to_zero();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -38,6 +38,7 @@ class UniformBuf {
|
|||
virtual ~UniformBuf();
|
||||
|
||||
virtual void update(const void *data) = 0;
|
||||
virtual void clear_to_zero() = 0;
|
||||
virtual void bind(int slot) = 0;
|
||||
virtual void bind_as_ssbo(int slot) = 0;
|
||||
virtual void unbind() = 0;
|
||||
|
|
|
@ -81,6 +81,14 @@ void MTLUniformBuf::update(const void *data)
|
|||
}
|
||||
}
|
||||
|
||||
void MTLUniformBuf::clear_to_zero()
|
||||
{
|
||||
/* TODO(fclem): Avoid another allocation and just do the clear on the GPU if possible. */
|
||||
void *clear_data = calloc(size_in_bytes_);
|
||||
this->update(clear_data);
|
||||
free(clear_data);
|
||||
}
|
||||
|
||||
void MTLUniformBuf::bind(int slot)
|
||||
{
|
||||
if (slot < 0) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue