Refactor: Store BLF Glyphs in blender::Map #118528
|
@ -259,6 +259,28 @@ else()
|
|||
set(WITH_UNITY_BUILD OFF)
|
||||
endif()
|
||||
|
||||
if(COMMAND target_precompile_headers)
|
||||
# Disabling is needed for `./tools/utils_maintenance/code_clean.py` to function.
|
||||
option(WITH_COMPILER_PRECOMPILED_HEADERS "\
|
||||
Use pre-compiled headers to speed up compilation."
|
||||
ON
|
||||
)
|
||||
mark_as_advanced(WITH_COMPILER_PRECOMPILED_HEADERS)
|
||||
|
||||
if(WITH_CLANG_TIDY AND CMAKE_COMPILER_IS_GNUCC)
|
||||
if(WITH_COMPILER_PRECOMPILED_HEADERS)
|
||||
message(STATUS
|
||||
"Clang-Tidy and GCC precompiled headers are incompatible, disabling precompiled headers"
|
||||
)
|
||||
set(WITH_COMPILER_PRECOMPILED_HEADERS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WITH_COMPILER_PRECOMPILED_HEADERS)
|
||||
set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_IK_ITASC "\
|
||||
Enable ITASC IK solver (only disable for development & for incompatible C++ compilers)"
|
||||
ON
|
||||
|
@ -759,8 +781,10 @@ if(WIN32)
|
|||
option(WITH_TBB_MALLOC_PROXY "Enable the TBB malloc replacement" ON)
|
||||
endif()
|
||||
|
||||
option(WITH_EXPERIMENTAL_FEATURES "Enable experimental features" ON)
|
||||
|
||||
# This should be turned off when Blender enter beta/rc/release
|
||||
if("${BLENDER_VERSION_CYCLE}" STREQUAL "alpha")
|
||||
if("${BLENDER_VERSION_CYCLE}" STREQUAL "alpha" AND WITH_EXPERIMENTAL_FEATURES)
|
||||
set(WITH_EXPERIMENTAL_FEATURES ON)
|
||||
else()
|
||||
set(WITH_EXPERIMENTAL_FEATURES OFF)
|
||||
|
|
|
@ -25,7 +25,12 @@ if EXIST %PYTHON% (
|
|||
)
|
||||
|
||||
if NOT EXIST %PYTHON% (
|
||||
echo Warning: Python not found, there is likely an issue with the library folder
|
||||
REM Only emit this warning when the library folder exists but the
|
||||
REM python folder does not. As we don't want to concern people that
|
||||
REM run make update for the first time.
|
||||
if EXIST %BLENDER_DIR%\..\lib\win64_vc15 (
|
||||
echo Warning: Python not found, there is likely an issue with the library folder
|
||||
)
|
||||
set PYTHON=""
|
||||
)
|
||||
|
||||
|
|
|
@ -135,6 +135,12 @@ static int gwl_registry_handler_interface_slot_max();
|
|||
static int gwl_registry_handler_interface_slot_from_string(const char *interface);
|
||||
static const GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(int interface_slot);
|
||||
|
||||
static bool xkb_compose_state_feed_and_get_utf8(
|
||||
xkb_compose_state *compose_state,
|
||||
xkb_state *state,
|
||||
const xkb_keycode_t key,
|
||||
char r_utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)]);
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
static void gwl_display_event_thread_destroy(GWL_Display *display);
|
||||
|
||||
|
@ -1103,6 +1109,37 @@ static void gwl_seat_key_layout_active_state_update_mask(GWL_Seat *seat)
|
|||
}
|
||||
}
|
||||
|
||||
/** Callback that runs from GHOST's timer. */
|
||||
static void gwl_seat_key_repeat_timer_fn(GHOST_ITimerTask *task, uint64_t time_ms)
|
||||
{
|
||||
GWL_KeyRepeatPlayload *payload = static_cast<GWL_KeyRepeatPlayload *>(task->getUserData());
|
||||
|
||||
GWL_Seat *seat = payload->seat;
|
||||
wl_surface *wl_surface_focus = seat->keyboard.wl.surface_window;
|
||||
if (UNLIKELY(wl_surface_focus == nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
|
||||
GHOST_SystemWayland *system = seat->system;
|
||||
const uint64_t event_ms = payload->time_ms_init + time_ms;
|
||||
/* Calculate this value every time in case modifier keys are pressed. */
|
||||
|
||||
char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
|
||||
if (seat->xkb.compose_state &&
|
||||
xkb_compose_state_feed_and_get_utf8(
|
||||
seat->xkb.compose_state, seat->xkb.state, payload->key_code, utf8_buf))
|
||||
{
|
||||
/* `utf8_buf` has been filled by a compose action. */
|
||||
}
|
||||
else {
|
||||
xkb_state_key_get_utf8(seat->xkb.state, payload->key_code, utf8_buf, sizeof(utf8_buf));
|
||||
}
|
||||
|
||||
system->pushEvent_maybe_pending(new GHOST_EventKey(
|
||||
event_ms, GHOST_kEventKeyDown, win, payload->key_data.gkey, true, utf8_buf));
|
||||
}
|
||||
|
||||
/**
|
||||
* \note Caller must lock `timer_mutex`.
|
||||
*/
|
||||
|
@ -2761,13 +2798,11 @@ static void keyboard_depressed_state_key_event(GWL_Seat *seat,
|
|||
}
|
||||
|
||||
static void keyboard_depressed_state_push_events_from_change(
|
||||
GWL_Seat *seat, const GWL_KeyboardDepressedState &key_depressed_prev)
|
||||
GWL_Seat *seat,
|
||||
GHOST_IWindow *win,
|
||||
const uint64_t event_ms,
|
||||
const GWL_KeyboardDepressedState &key_depressed_prev)
|
||||
{
|
||||
GHOST_IWindow *win = ghost_wl_surface_user_data(seat->keyboard.wl.surface_window);
|
||||
const GHOST_SystemWayland *system = seat->system;
|
||||
/* Caller has no time-stamp, set from system. */
|
||||
const uint64_t event_ms = system->getMilliSeconds();
|
||||
|
||||
/* Separate key up and down into separate passes so key down events always come after key up.
|
||||
* Do this so users of GHOST can use the last pressed or released modifier to check
|
||||
* if the modifier is held instead of counting modifiers pressed as is done here,
|
||||
|
@ -4718,6 +4753,8 @@ static void keyboard_handle_enter(void *data,
|
|||
CLOG_INFO(LOG, 2, "enter");
|
||||
|
||||
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
|
||||
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface);
|
||||
|
||||
seat->keyboard.serial = serial;
|
||||
seat->keyboard.wl.surface_window = wl_surface;
|
||||
|
||||
|
@ -4729,6 +4766,12 @@ static void keyboard_handle_enter(void *data,
|
|||
GWL_KeyboardDepressedState key_depressed_prev = seat->key_depressed;
|
||||
keyboard_depressed_state_reset(seat);
|
||||
|
||||
/* Keep track of the last held repeating key, start the repeat timer if one exists. */
|
||||
struct {
|
||||
uint32_t key = std::numeric_limits<uint32_t>::max();
|
||||
xkb_keysym_t sym = 0;
|
||||
} repeat;
|
||||
|
||||
uint32_t *key;
|
||||
WL_ARRAY_FOR_EACH (key, keys) {
|
||||
const xkb_keycode_t key_code = *key + EVDEV_OFFSET;
|
||||
|
@ -4738,9 +4781,41 @@ static void keyboard_handle_enter(void *data,
|
|||
if (gkey != GHOST_kKeyUnknown) {
|
||||
keyboard_depressed_state_key_event(seat, gkey, GHOST_kEventKeyDown);
|
||||
}
|
||||
|
||||
if (xkb_keymap_key_repeats(xkb_state_get_keymap(seat->xkb.state), key_code)) {
|
||||
repeat.key = *key;
|
||||
repeat.sym = sym;
|
||||
}
|
||||
}
|
||||
|
||||
keyboard_depressed_state_push_events_from_change(seat, key_depressed_prev);
|
||||
/* Caller has no time-stamp, set from system. */
|
||||
const uint64_t event_ms = seat->system->getMilliSeconds();
|
||||
keyboard_depressed_state_push_events_from_change(seat, win, event_ms, key_depressed_prev);
|
||||
|
||||
if ((repeat.key != std::numeric_limits<uint32_t>::max()) && (seat->key_repeat.rate > 0)) {
|
||||
/* Since the key has been held, immediately send a press event.
|
||||
* This also ensures the key will be registered as pressed, see #117896. */
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
/* Should have been cleared on leave, set here just in case. */
|
||||
if (UNLIKELY(seat->key_repeat.timer)) {
|
||||
keyboard_handle_key_repeat_cancel(seat);
|
||||
}
|
||||
|
||||
const xkb_keycode_t key_code = repeat.key + EVDEV_OFFSET;
|
||||
const GHOST_TKey gkey = xkb_map_gkey_or_scan_code(repeat.sym, repeat.key);
|
||||
|
||||
GWL_KeyRepeatPlayload *key_repeat_payload = new GWL_KeyRepeatPlayload();
|
||||
key_repeat_payload->seat = seat;
|
||||
key_repeat_payload->key_code = key_code;
|
||||
key_repeat_payload->key_data.gkey = gkey;
|
||||
|
||||
gwl_seat_key_repeat_timer_add(seat, gwl_seat_key_repeat_timer_fn, key_repeat_payload, false);
|
||||
/* Ensure there is a press event on enter so this is known to be held before any mouse
|
||||
* button events which may use a key-binding that depends on this key being held. */
|
||||
gwl_seat_key_repeat_timer_fn(seat->key_repeat.timer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5031,33 +5106,7 @@ static void keyboard_handle_key(void *data,
|
|||
}
|
||||
|
||||
if (key_repeat_payload) {
|
||||
auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t time_ms) {
|
||||
GWL_KeyRepeatPlayload *payload = static_cast<GWL_KeyRepeatPlayload *>(task->getUserData());
|
||||
|
||||
GWL_Seat *seat = payload->seat;
|
||||
if (wl_surface *wl_surface_focus = seat->keyboard.wl.surface_window) {
|
||||
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
|
||||
GHOST_SystemWayland *system = seat->system;
|
||||
const uint64_t event_ms = payload->time_ms_init + time_ms;
|
||||
/* Calculate this value every time in case modifier keys are pressed. */
|
||||
|
||||
char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
|
||||
if (seat->xkb.compose_state &&
|
||||
xkb_compose_state_feed_and_get_utf8(
|
||||
seat->xkb.compose_state, seat->xkb.state, payload->key_code, utf8_buf))
|
||||
{
|
||||
/* `utf8_buf` has been filled by a compose action. */
|
||||
}
|
||||
else {
|
||||
xkb_state_key_get_utf8(seat->xkb.state, payload->key_code, utf8_buf, sizeof(utf8_buf));
|
||||
}
|
||||
|
||||
system->pushEvent_maybe_pending(new GHOST_EventKey(
|
||||
event_ms, GHOST_kEventKeyDown, win, payload->key_data.gkey, true, utf8_buf));
|
||||
}
|
||||
};
|
||||
|
||||
gwl_seat_key_repeat_timer_add(seat, key_repeat_fn, key_repeat_payload, true);
|
||||
gwl_seat_key_repeat_timer_add(seat, gwl_seat_key_repeat_timer_fn, key_repeat_payload, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -341,6 +341,10 @@ template<typename T> inline T *MEM_cnew(const char *allocation_name, const T &ot
|
|||
{ \
|
||||
return MEM_mallocN(num_bytes, _id); \
|
||||
} \
|
||||
void *operator new(size_t num_bytes, std::align_val_t alignment) \
|
||||
{ \
|
||||
return MEM_mallocN_aligned(num_bytes, size_t(alignment), _id); \
|
||||
} \
|
||||
void operator delete(void *mem) \
|
||||
{ \
|
||||
if (mem) { \
|
||||
|
@ -351,6 +355,10 @@ template<typename T> inline T *MEM_cnew(const char *allocation_name, const T &ot
|
|||
{ \
|
||||
return MEM_mallocN(num_bytes, _id "[]"); \
|
||||
} \
|
||||
void *operator new[](size_t num_bytes, std::align_val_t alignment) \
|
||||
{ \
|
||||
return MEM_mallocN_aligned(num_bytes, size_t(alignment), _id "[]"); \
|
||||
} \
|
||||
void operator delete[](void *mem) \
|
||||
{ \
|
||||
if (mem) { \
|
||||
|
|
|
@ -427,9 +427,9 @@ class PREFERENCES_OT_keyconfig_remove(Operator):
|
|||
# Add-on Operators
|
||||
|
||||
class PREFERENCES_OT_addon_enable(Operator):
|
||||
"""Enable an add-on"""
|
||||
"""Turn on this extension"""
|
||||
bl_idname = "preferences.addon_enable"
|
||||
bl_label = "Enable Add-on"
|
||||
bl_label = "Enable Extension"
|
||||
|
||||
module: StringProperty(
|
||||
name="Module",
|
||||
|
@ -473,9 +473,9 @@ class PREFERENCES_OT_addon_enable(Operator):
|
|||
|
||||
|
||||
class PREFERENCES_OT_addon_disable(Operator):
|
||||
"""Disable an add-on"""
|
||||
"""Turn off this extension"""
|
||||
bl_idname = "preferences.addon_disable"
|
||||
bl_label = "Disable Add-on"
|
||||
bl_label = "Disable Extension"
|
||||
|
||||
module: StringProperty(
|
||||
name="Module",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
from rna_prop_ui import PropertyPanel
|
||||
import rna_prop_ui
|
||||
|
||||
from bpy.app.translations import contexts as i18n_contexts
|
||||
|
||||
|
@ -260,6 +260,16 @@ class BONE_PT_collections(BoneButtonsPanel, Panel):
|
|||
layout.use_property_split = False
|
||||
|
||||
bone = context.bone or context.edit_bone
|
||||
object = context.pose_object or context.edit_object or context.object
|
||||
if not object:
|
||||
layout.active = False
|
||||
sub = layout.column(align=True)
|
||||
sub.label(text="Cannot figure out which object this bone belongs to.")
|
||||
sub.label(text="Please file a bug report.")
|
||||
return
|
||||
|
||||
armature = object.data
|
||||
is_solo_active = armature.collections.is_solo_active
|
||||
|
||||
if not bone.collections:
|
||||
layout.active = False
|
||||
|
@ -275,8 +285,15 @@ class BONE_PT_collections(BoneButtonsPanel, Panel):
|
|||
# Name & visibility of bcoll. Safe things, so aligned together.
|
||||
row = bcoll_row.row(align=True)
|
||||
row.label(text=bcoll.name)
|
||||
row.prop(bcoll, "is_visible", text="",
|
||||
icon='HIDE_OFF' if bcoll.is_visible else 'HIDE_ON')
|
||||
|
||||
# Sub-layout that's dimmed when the bone collection's own visibility flag doesn't matter.
|
||||
sub_visible = row.row(align=True)
|
||||
sub_visible.active = (not is_solo_active) and bcoll.is_visible_ancestors
|
||||
sub_visible.prop(bcoll, "is_visible", text="",
|
||||
icon='HIDE_OFF' if bcoll.is_visible else 'HIDE_ON')
|
||||
|
||||
row.prop(bcoll, "is_solo", text="",
|
||||
icon='SOLO_ON' if bcoll.is_solo else 'SOLO_OFF')
|
||||
|
||||
# Unassignment operator, less safe so with a bit of spacing.
|
||||
props = bcoll_row.operator("armature.collection_unassign_named",
|
||||
|
@ -530,7 +547,7 @@ class BONE_PT_deform(BoneButtonsPanel, Panel):
|
|||
col.prop(bone, "tail_radius", text="Tail")
|
||||
|
||||
|
||||
class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
|
||||
class BONE_PT_custom_props(BoneButtonsPanel, rna_prop_ui.PropertyPanel, Panel):
|
||||
COMPAT_ENGINES = {
|
||||
'BLENDER_RENDER',
|
||||
'BLENDER_EEVEE',
|
||||
|
@ -539,14 +556,45 @@ class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
|
|||
}
|
||||
_property_type = bpy.types.Bone, bpy.types.EditBone, bpy.types.PoseBone
|
||||
|
||||
@property
|
||||
def _context_path(self):
|
||||
obj = bpy.context.object
|
||||
if obj and obj.mode == 'POSE':
|
||||
return "active_pose_bone"
|
||||
else:
|
||||
@classmethod
|
||||
def _poll(cls, context):
|
||||
context_path = cls._get_context_path(context)
|
||||
rna_item, _context_member = rna_prop_ui.rna_idprop_context_value(
|
||||
context, context_path, cls._property_type)
|
||||
return bool(rna_item)
|
||||
|
||||
def draw(self, context):
|
||||
context_path = self._get_context_path(context)
|
||||
rna_prop_ui.draw(self.layout, context, context_path, self._property_type)
|
||||
|
||||
@classmethod
|
||||
def _get_context_path(self, context):
|
||||
obj = context.object
|
||||
if not obj:
|
||||
# We have to return _something_. If there is some bone by some
|
||||
# miracle, just use it.
|
||||
return "bone"
|
||||
|
||||
if obj.mode != 'POSE':
|
||||
# Outside of pose mode, active_bone is the one to use. It's either a
|
||||
# Bone or an EditBone, depending on the mode.
|
||||
return "active_bone"
|
||||
|
||||
if context.active_pose_bone is not None:
|
||||
# There is an active pose bone, so use it.
|
||||
return "active_pose_bone"
|
||||
|
||||
# When the active bone is hidden, `context.active_pose_bone` is None, but
|
||||
# `context.bone` still points to it. Use that to still get the pose bone.
|
||||
if context.bone is None:
|
||||
# If there is no active bone, let the rest of the code refer to the
|
||||
# also-None active pose bone, as that's more appropriate given we're
|
||||
# currently in pose mode.
|
||||
return "active_pose_bone"
|
||||
|
||||
bone_path = obj.pose.bones[context.bone.name].path_from_id()
|
||||
return f"object.{bone_path}"
|
||||
|
||||
|
||||
classes = (
|
||||
BONE_PT_context_bone,
|
||||
|
|
|
@ -196,6 +196,7 @@ class OBJECT_MT_modifier_add_deform(ModifierAddMenu, Menu):
|
|||
if ob_type == 'VOLUME':
|
||||
self.operator_modifier_add(layout, 'VOLUME_DISPLACE')
|
||||
if ob_type == 'GREASEPENCIL':
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_HOOK')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_LATTICE')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_NOISE')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET')
|
||||
|
|
|
@ -106,7 +106,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
|
|||
)
|
||||
|
||||
|
||||
# cache-type can be 'PSYS' 'HAIR' 'FLUID' etc.
|
||||
# cache-type can be 'PSYS' 'HAIR' etc. ('FLUID' uses its own cache)
|
||||
|
||||
def point_cache_ui(self, cache, enabled, cachetype):
|
||||
layout = self.layout
|
||||
|
@ -130,12 +130,8 @@ def point_cache_ui(self, cache, enabled, cachetype):
|
|||
col.operator("ptcache.add", icon='ADD', text="")
|
||||
col.operator("ptcache.remove", icon='REMOVE', text="")
|
||||
|
||||
if cachetype in {'PSYS', 'HAIR', 'FLUID'}:
|
||||
if cachetype in {'PSYS', 'HAIR'}:
|
||||
col = layout.column()
|
||||
|
||||
if cachetype == 'FLUID':
|
||||
col.prop(cache, "use_library_path", text="Use Library Path")
|
||||
|
||||
col.prop(cache, "use_external")
|
||||
|
||||
if cache.use_external:
|
||||
|
@ -149,14 +145,14 @@ def point_cache_ui(self, cache, enabled, cachetype):
|
|||
col.alignment = 'RIGHT'
|
||||
col.label(text=cache_info)
|
||||
else:
|
||||
if cachetype in {'FLUID', 'DYNAMIC_PAINT'}:
|
||||
if cachetype == 'DYNAMIC_PAINT':
|
||||
if not is_saved:
|
||||
col = layout.column(align=True)
|
||||
col.alignment = 'RIGHT'
|
||||
col.label(text="Cache is disabled until the file is saved")
|
||||
layout.enabled = False
|
||||
|
||||
if not cache.use_external or cachetype == 'FLUID':
|
||||
if not cache.use_external:
|
||||
col = layout.column(align=True)
|
||||
|
||||
if cachetype not in {'PSYS', 'DYNAMIC_PAINT'}:
|
||||
|
@ -164,18 +160,18 @@ def point_cache_ui(self, cache, enabled, cachetype):
|
|||
col.prop(cache, "frame_start", text="Simulation Start")
|
||||
col.prop(cache, "frame_end")
|
||||
|
||||
if cachetype not in {'FLUID', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
|
||||
if cachetype not in {'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
|
||||
col.prop(cache, "frame_step")
|
||||
|
||||
cache_info = cache.info
|
||||
if cachetype != 'FLUID' and cache_info: # avoid empty space.
|
||||
if cache_info: # avoid empty space.
|
||||
col = layout.column(align=True)
|
||||
col.alignment = 'RIGHT'
|
||||
col.label(text=cache_info)
|
||||
|
||||
can_bake = True
|
||||
|
||||
if cachetype not in {'FLUID', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
|
||||
if cachetype not in {'DYNAMIC_PAINT', 'RIGID_BODY'}:
|
||||
if not is_saved:
|
||||
col = layout.column(align=True)
|
||||
col.alignment = 'RIGHT'
|
||||
|
|
|
@ -1597,7 +1597,7 @@ class USERPREF_UL_asset_libraries(UIList):
|
|||
class USERPREF_UL_extension_repos(UIList):
|
||||
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
|
||||
repo = item
|
||||
icon = 'WORLD' if repo.use_remote_path else 'DISK_DRIVE'
|
||||
icon = 'NETWORK_DRIVE' if repo.use_remote_path else 'DISK_DRIVE'
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
layout.prop(repo, "name", text="", icon=icon, emboss=False)
|
||||
elif self.layout_type == 'GRID':
|
||||
|
@ -1614,6 +1614,24 @@ class USERPREF_UL_extension_repos(UIList):
|
|||
|
||||
layout.prop(repo, "enabled", text="", emboss=False, icon='CHECKBOX_HLT' if repo.enabled else 'CHECKBOX_DEHLT')
|
||||
|
||||
def filter_items(self, _context, data, propname):
|
||||
# Repositories has no index, converting to a list.
|
||||
items = list(getattr(data, propname))
|
||||
|
||||
flags = [self.bitflag_filter_item] * len(items)
|
||||
|
||||
indices = [None] * len(items)
|
||||
for index, orig_index in enumerate(sorted(
|
||||
range(len(items)),
|
||||
key=lambda i: (
|
||||
items[i].use_remote_path is False,
|
||||
items[i].name.lower(),
|
||||
)
|
||||
)):
|
||||
indices[orig_index] = index
|
||||
|
||||
return flags, indices
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Save/Load Panels
|
||||
|
|
|
@ -7,14 +7,6 @@ if(WITH_LEGACY_OPENGL)
|
|||
endif()
|
||||
|
||||
if(WITH_CLANG_TIDY AND NOT MSVC)
|
||||
if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
message(WARNING "Currently Clang-Tidy might fail with GCC toolchain, switch to Clang toolchain if that happens")
|
||||
if(COMMAND target_precompile_headers)
|
||||
message(STATUS "Clang-Tidy and GCC precompiled headers are incompatible, disabling precompiled headers")
|
||||
set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(ClangTidy REQUIRED)
|
||||
set(CMAKE_C_CLANG_TIDY
|
||||
${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option)
|
||||
|
|
|
@ -179,6 +179,7 @@ class AssetLibrary {
|
|||
Vector<AssetLibraryReference> all_valid_asset_library_refs();
|
||||
|
||||
AssetLibraryReference all_library_reference();
|
||||
void all_library_reload_catalogs_if_dirty();
|
||||
|
||||
} // namespace blender::asset_system
|
||||
|
||||
|
|
|
@ -238,7 +238,7 @@ void AssetCatalogService::prune_catalogs_by_path(const AssetCatalogPath &path)
|
|||
}
|
||||
|
||||
this->rebuild_tree();
|
||||
AssetLibraryService::get()->rebuild_all_library();
|
||||
AssetLibraryService::get()->tag_all_library_catalogs_dirty();
|
||||
}
|
||||
|
||||
void AssetCatalogService::prune_catalogs_by_id(const CatalogID catalog_id)
|
||||
|
@ -273,7 +273,7 @@ void AssetCatalogService::update_catalog_path(const CatalogID catalog_id,
|
|||
}
|
||||
|
||||
this->rebuild_tree();
|
||||
AssetLibraryService::get()->rebuild_all_library();
|
||||
AssetLibraryService::get()->tag_all_library_catalogs_dirty();
|
||||
}
|
||||
|
||||
AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalog_path)
|
||||
|
@ -299,8 +299,7 @@ AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalo
|
|||
|
||||
BLI_assert_msg(catalog_tree_, "An Asset Catalog tree should always exist.");
|
||||
catalog_tree_->insert_item(*catalog_ptr);
|
||||
|
||||
AssetLibraryService::get()->rebuild_all_library();
|
||||
AssetLibraryService::get()->tag_all_library_catalogs_dirty();
|
||||
|
||||
return catalog_ptr;
|
||||
}
|
||||
|
@ -655,7 +654,7 @@ void AssetCatalogService::undo()
|
|||
redo_snapshots_.append(std::move(catalog_collection_));
|
||||
catalog_collection_ = undo_snapshots_.pop_last();
|
||||
this->rebuild_tree();
|
||||
AssetLibraryService::get()->rebuild_all_library();
|
||||
AssetLibraryService::get()->tag_all_library_catalogs_dirty();
|
||||
}
|
||||
|
||||
void AssetCatalogService::redo()
|
||||
|
@ -666,7 +665,7 @@ void AssetCatalogService::redo()
|
|||
undo_snapshots_.append(std::move(catalog_collection_));
|
||||
catalog_collection_ = redo_snapshots_.pop_last();
|
||||
this->rebuild_tree();
|
||||
AssetLibraryService::get()->rebuild_all_library();
|
||||
AssetLibraryService::get()->tag_all_library_catalogs_dirty();
|
||||
}
|
||||
|
||||
void AssetCatalogService::undo_push()
|
||||
|
|
|
@ -340,4 +340,10 @@ AssetLibraryReference all_library_reference()
|
|||
return all_library_ref;
|
||||
}
|
||||
|
||||
void all_library_reload_catalogs_if_dirty()
|
||||
{
|
||||
AssetLibraryService *service = AssetLibraryService::get();
|
||||
service->reload_all_library_catalogs_if_dirty();
|
||||
}
|
||||
|
||||
} // namespace blender::asset_system
|
||||
|
|
|
@ -57,7 +57,19 @@ void AllAssetLibrary::rebuild_catalogs_from_nested(const bool reload_nested_cata
|
|||
false);
|
||||
|
||||
new_catalog_service->rebuild_tree();
|
||||
|
||||
this->catalog_service = std::move(new_catalog_service);
|
||||
catalogs_dirty_ = false;
|
||||
}
|
||||
|
||||
void AllAssetLibrary::tag_catalogs_dirty()
|
||||
{
|
||||
catalogs_dirty_ = true;
|
||||
}
|
||||
|
||||
bool AllAssetLibrary::is_catalogs_dirty()
|
||||
{
|
||||
return catalogs_dirty_;
|
||||
}
|
||||
|
||||
void AllAssetLibrary::refresh_catalogs()
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
namespace blender::asset_system {
|
||||
|
||||
class AllAssetLibrary : public AssetLibrary {
|
||||
bool catalogs_dirty_ = true;
|
||||
|
||||
public:
|
||||
AllAssetLibrary();
|
||||
|
||||
|
@ -26,6 +28,9 @@ class AllAssetLibrary : public AssetLibrary {
|
|||
* merge them into the in-memory representations.
|
||||
*/
|
||||
void rebuild_catalogs_from_nested(bool reload_nested_catalogs);
|
||||
|
||||
void tag_catalogs_dirty();
|
||||
bool is_catalogs_dirty();
|
||||
};
|
||||
|
||||
} // namespace blender::asset_system
|
||||
|
|
|
@ -187,10 +187,17 @@ AssetLibrary *AssetLibraryService::get_asset_library_current_file()
|
|||
return lib;
|
||||
}
|
||||
|
||||
void AssetLibraryService::rebuild_all_library()
|
||||
void AssetLibraryService::tag_all_library_catalogs_dirty()
|
||||
{
|
||||
if (all_library_) {
|
||||
all_library_->rebuild_catalogs_from_nested(false);
|
||||
all_library_->tag_catalogs_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetLibraryService::reload_all_library_catalogs_if_dirty()
|
||||
{
|
||||
if (all_library_ && all_library_->is_catalogs_dirty()) {
|
||||
all_library_->refresh_catalogs();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,13 @@ class AssetLibraryService {
|
|||
AssetLibrary *get_asset_library_current_file();
|
||||
/** Get the "All" asset library, which loads all others and merges them into one. */
|
||||
AssetLibrary *get_asset_library_all(const Main *bmain);
|
||||
void rebuild_all_library();
|
||||
/**
|
||||
* Tag the "All" asset library as needing to reload catalogs. This should be called when catalog
|
||||
* data of other asset libraries changes. Note that changes to the catalog definition file on
|
||||
* disk don't ever affect this "dirty" flag. It only reflects changes from this Blender session.
|
||||
*/
|
||||
void tag_all_library_catalogs_dirty();
|
||||
void reload_all_library_catalogs_if_dirty();
|
||||
|
||||
/**
|
||||
* Return the start position of the last blend-file extension in given path,
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
/* Name of sub-directory inside #BLENDER_DATAFILES that contains font files. */
|
||||
#define BLF_DATAFILES_FONTS_DIR "fonts"
|
||||
|
@ -254,6 +256,10 @@ void BLF_rotation(int fontid, float angle);
|
|||
void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax);
|
||||
void BLF_wordwrap(int fontid, int wrap_width);
|
||||
|
||||
blender::Vector<blender::StringRef> BLF_string_wrap(int fontid,
|
||||
blender::StringRef str,
|
||||
const int max_pixel_width);
|
||||
|
||||
#if BLF_BLUR_ENABLE
|
||||
void BLF_blur(int fontid, int size);
|
||||
#endif
|
||||
|
|
|
@ -935,6 +935,17 @@ void BLF_draw_buffer(int fontid, const char *str, const size_t str_len)
|
|||
BLF_draw_buffer_ex(fontid, str, str_len, nullptr);
|
||||
}
|
||||
|
||||
blender::Vector<blender::StringRef> BLF_string_wrap(int fontid,
|
||||
blender::StringRef str,
|
||||
const int max_pixel_width)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
if (!font) {
|
||||
return {};
|
||||
}
|
||||
return blf_font_string_wrap(font, str, max_pixel_width);
|
||||
}
|
||||
|
||||
char *BLF_display_name_from_file(const char *filepath)
|
||||
{
|
||||
/* While listing font directories this function can be called simultaneously from a greater
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "BLI_string_cursor_utf8.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
|
||||
|
@ -1085,6 +1086,7 @@ void blf_str_offset_to_glyph_bounds(FontBLF *font,
|
|||
static void blf_font_wrap_apply(FontBLF *font,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
const int max_pixel_width,
|
||||
ResultBLF *r_info,
|
||||
void (*callback)(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
|
@ -1109,7 +1111,7 @@ static void blf_font_wrap_apply(FontBLF *font,
|
|||
struct WordWrapVars {
|
||||
ft_pix wrap_width;
|
||||
size_t start, last[2];
|
||||
} wrap = {font->wrap_width != -1 ? ft_pix_from_int(font->wrap_width) : INT_MAX, 0, {0, 0}};
|
||||
} wrap = {max_pixel_width != -1 ? ft_pix_from_int(max_pixel_width) : INT_MAX, 0, {0, 0}};
|
||||
|
||||
// printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str);
|
||||
while ((i < str_len) && str[i]) {
|
||||
|
@ -1198,7 +1200,8 @@ static void blf_font_draw__wrap_cb(FontBLF *font,
|
|||
}
|
||||
void blf_font_draw__wrap(FontBLF *font, const char *str, const size_t str_len, ResultBLF *r_info)
|
||||
{
|
||||
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw__wrap_cb, nullptr);
|
||||
blf_font_wrap_apply(
|
||||
font, str, str_len, font->wrap_width, r_info, blf_font_draw__wrap_cb, nullptr);
|
||||
}
|
||||
|
||||
/** Utility for #blf_font_boundbox__wrap. */
|
||||
|
@ -1223,7 +1226,8 @@ void blf_font_boundbox__wrap(
|
|||
box->ymin = 32000;
|
||||
box->ymax = -32000;
|
||||
|
||||
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_boundbox_wrap_cb, box);
|
||||
blf_font_wrap_apply(
|
||||
font, str, str_len, font->wrap_width, r_info, blf_font_boundbox_wrap_cb, box);
|
||||
}
|
||||
|
||||
/** Utility for #blf_font_draw_buffer__wrap. */
|
||||
|
@ -1241,7 +1245,37 @@ void blf_font_draw_buffer__wrap(FontBLF *font,
|
|||
const size_t str_len,
|
||||
ResultBLF *r_info)
|
||||
{
|
||||
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw_buffer__wrap_cb, nullptr);
|
||||
blf_font_wrap_apply(
|
||||
font, str, str_len, font->wrap_width, r_info, blf_font_draw_buffer__wrap_cb, nullptr);
|
||||
}
|
||||
|
||||
/** Wrap a blender::StringRef. */
|
||||
static void blf_font_string_wrap_cb(FontBLF * /*font*/,
|
||||
GlyphCacheBLF * /*gc*/,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
ft_pix /*pen_y*/,
|
||||
void *str_list_ptr)
|
||||
{
|
||||
blender::Vector<blender::StringRef> *list = static_cast<blender::Vector<blender::StringRef> *>(
|
||||
str_list_ptr);
|
||||
blender::StringRef line(str, str + str_len);
|
||||
list->append(line);
|
||||
}
|
||||
|
||||
blender::Vector<blender::StringRef> blf_font_string_wrap(FontBLF *font,
|
||||
blender::StringRef str,
|
||||
int max_pixel_width)
|
||||
{
|
||||
blender::Vector<blender::StringRef> list;
|
||||
blf_font_wrap_apply(font,
|
||||
str.data(),
|
||||
size_t(str.size()),
|
||||
max_pixel_width,
|
||||
nullptr,
|
||||
blf_font_string_wrap_cb,
|
||||
&list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
struct FontBLF;
|
||||
struct GlyphBLF;
|
||||
struct GlyphCacheBLF;
|
||||
|
@ -94,6 +97,10 @@ void blf_font_draw__wrap(struct FontBLF *font,
|
|||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
|
||||
blender::Vector<blender::StringRef> blf_font_string_wrap(FontBLF *font,
|
||||
blender::StringRef str,
|
||||
int max_pixel_width);
|
||||
|
||||
/**
|
||||
* Use fixed column width, but an utf8 character may occupy multiple columns.
|
||||
*/
|
||||
|
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 5
|
||||
#define BLENDER_FILE_SUBVERSION 6
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -110,6 +110,7 @@ enum eCbEvent {
|
|||
BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST,
|
||||
BKE_CB_EVT_EXTENSION_REPOS_SYNC,
|
||||
BKE_CB_EVT_EXTENSION_REPOS_UPGRADE,
|
||||
BKE_CB_EVT_EXTENSION_DROP_URL,
|
||||
BKE_CB_EVT_TOT,
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include <array>
|
||||
|
@ -324,15 +325,24 @@ void BKE_library_ID_test_usages(Main *bmain,
|
|||
/** Parameters and result data structure for the 'unused IDs' functions below. */
|
||||
struct LibQueryUnusedIDsData {
|
||||
/** Process local data-blocks. */
|
||||
bool do_local_ids;
|
||||
bool do_local_ids = false;
|
||||
/** Process linked data-blocks. */
|
||||
bool do_linked_ids;
|
||||
bool do_linked_ids = false;
|
||||
/**
|
||||
* Process all actually unused data-blocks, including these that are currently only used by
|
||||
* other unused data-blocks, and 'dependency islands' of several data-blocks using each-other,
|
||||
* without any external valid user.
|
||||
*/
|
||||
bool do_recursive;
|
||||
bool do_recursive = false;
|
||||
|
||||
/**
|
||||
* Callback filter, if defined and it returns `true`, the given `id` may be considered as unused,
|
||||
* otherwise it will always be considered as used.
|
||||
*
|
||||
* Allows for more complex handling of which IDs should be deleted, on top of the basic
|
||||
* local/linked choices.
|
||||
*/
|
||||
blender::FunctionRef<bool(ID *id)> filter_fn = nullptr;
|
||||
|
||||
/**
|
||||
* Amount of detected as unused data-blocks, per type and total as the last value of the array
|
||||
|
|
|
@ -84,6 +84,8 @@ bUserExtensionRepo *BKE_preferences_extension_repo_add(UserDef *userdef,
|
|||
const char *module,
|
||||
const char *custom_dirpath);
|
||||
void BKE_preferences_extension_repo_remove(UserDef *userdef, bUserExtensionRepo *repo);
|
||||
bUserExtensionRepo *BKE_preferences_extension_repo_add_default(UserDef *userdef);
|
||||
bUserExtensionRepo *BKE_preferences_extension_repo_add_default_user(UserDef *userdef);
|
||||
|
||||
void BKE_preferences_extension_repo_name_set(UserDef *userdef,
|
||||
bUserExtensionRepo *repo,
|
||||
|
|
|
@ -41,24 +41,24 @@ struct ReportList;
|
|||
struct Scene;
|
||||
struct SwsContext;
|
||||
|
||||
int BKE_ffmpeg_start(void *context_v,
|
||||
const Scene *scene,
|
||||
RenderData *rd,
|
||||
int rectx,
|
||||
int recty,
|
||||
ReportList *reports,
|
||||
bool preview,
|
||||
const char *suffix);
|
||||
void BKE_ffmpeg_end(void *context_v);
|
||||
int BKE_ffmpeg_append(void *context_v,
|
||||
struct ImBuf;
|
||||
|
||||
bool BKE_ffmpeg_start(void *context_v,
|
||||
const Scene *scene,
|
||||
RenderData *rd,
|
||||
int start_frame,
|
||||
int frame,
|
||||
int *pixels,
|
||||
int rectx,
|
||||
int recty,
|
||||
const char *suffix,
|
||||
ReportList *reports);
|
||||
ReportList *reports,
|
||||
bool preview,
|
||||
const char *suffix);
|
||||
void BKE_ffmpeg_end(void *context_v);
|
||||
bool BKE_ffmpeg_append(void *context_v,
|
||||
RenderData *rd,
|
||||
int start_frame,
|
||||
int frame,
|
||||
const ImBuf *image,
|
||||
const char *suffix,
|
||||
ReportList *reports);
|
||||
void BKE_ffmpeg_filepath_get(char filepath[/*FILE_MAX*/ 1024],
|
||||
const RenderData *rd,
|
||||
bool preview,
|
||||
|
|
|
@ -10,28 +10,27 @@
|
|||
|
||||
/* generic blender movie support, could move to own module */
|
||||
|
||||
struct ImBuf;
|
||||
struct RenderData;
|
||||
struct ReportList;
|
||||
struct Scene;
|
||||
|
||||
struct bMovieHandle {
|
||||
int (*start_movie)(void *context_v,
|
||||
const Scene *scene,
|
||||
RenderData *rd,
|
||||
int rectx,
|
||||
int recty,
|
||||
ReportList *reports,
|
||||
bool preview,
|
||||
const char *suffix);
|
||||
int (*append_movie)(void *context_v,
|
||||
bool (*start_movie)(void *context_v,
|
||||
const Scene *scene,
|
||||
RenderData *rd,
|
||||
int start_frame,
|
||||
int frame,
|
||||
int *pixels,
|
||||
int rectx,
|
||||
int recty,
|
||||
const char *suffix,
|
||||
ReportList *reports);
|
||||
ReportList *reports,
|
||||
bool preview,
|
||||
const char *suffix);
|
||||
bool (*append_movie)(void *context_v,
|
||||
RenderData *rd,
|
||||
int start_frame,
|
||||
int frame,
|
||||
const ImBuf *image,
|
||||
const char *suffix,
|
||||
ReportList *reports);
|
||||
void (*end_movie)(void *context_v);
|
||||
|
||||
/* Optional function. */
|
||||
|
|
|
@ -1225,6 +1225,9 @@ UserDef *BKE_blendfile_userdef_from_defaults()
|
|||
|
||||
BKE_preferences_asset_library_default_add(userdef);
|
||||
|
||||
BKE_preferences_extension_repo_add_default(userdef);
|
||||
BKE_preferences_extension_repo_add_default_user(userdef);
|
||||
|
||||
return userdef;
|
||||
}
|
||||
|
||||
|
|
|
@ -238,7 +238,6 @@ static ID **collection_owner_pointer_get(ID *id)
|
|||
if ((id->flag & LIB_EMBEDDED_DATA) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0);
|
||||
|
||||
Collection *master_collection = (Collection *)id;
|
||||
BLI_assert((master_collection->flag & COLLECTION_IS_MASTER) != 0);
|
||||
|
|
|
@ -963,6 +963,7 @@ void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb,
|
|||
BLO_read_data_address(reader, &hmd->curfalloff);
|
||||
if (hmd->curfalloff) {
|
||||
BKE_curvemapping_blend_read(reader, hmd->curfalloff);
|
||||
BKE_curvemapping_init(hmd->curfalloff);
|
||||
}
|
||||
}
|
||||
else if (md->type == eGpencilModifierType_Noise) {
|
||||
|
|
|
@ -1708,6 +1708,13 @@ template<typename T> static void shrink_array(T **array, int *num, const int shr
|
|||
{
|
||||
BLI_assert(shrink_num > 0);
|
||||
const int new_array_num = *num - shrink_num;
|
||||
if (new_array_num == 0) {
|
||||
MEM_freeN(*array);
|
||||
*array = nullptr;
|
||||
*num = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
T *new_array = reinterpret_cast<T *>(MEM_cnew_array<T *>(new_array_num, __func__));
|
||||
|
||||
blender::uninitialized_move_n(*array, new_array_num, new_array);
|
||||
|
@ -1924,6 +1931,7 @@ static void remove_drawings_unchecked(GreasePencil &grease_pencil,
|
|||
for (auto [key, value] : layer->frames_for_write().items()) {
|
||||
if (value.drawing_index == swap_index) {
|
||||
value.drawing_index = index_to_remove;
|
||||
layer->tag_frames_map_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2521,7 +2529,7 @@ void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
|
|||
layer.parent_group().unlink_node(layer.as_node());
|
||||
|
||||
/* Remove drawings. */
|
||||
for (GreasePencilFrame frame : layer.frames_for_write().values()) {
|
||||
for (const GreasePencilFrame frame : layer.frames().values()) {
|
||||
GreasePencilDrawingBase *drawing_base = this->drawing(frame.drawing_index);
|
||||
if (drawing_base->type != GP_DRAWING) {
|
||||
continue;
|
||||
|
@ -2598,7 +2606,8 @@ static void write_drawing_array(GreasePencil &grease_pencil, BlendWriter *writer
|
|||
|
||||
static void free_drawing_array(GreasePencil &grease_pencil)
|
||||
{
|
||||
if (grease_pencil.drawing_array == nullptr || grease_pencil.drawing_array_num == 0) {
|
||||
if (grease_pencil.drawing_array == nullptr) {
|
||||
BLI_assert(grease_pencil.drawing_array_num == 0);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < grease_pencil.drawing_array_num; i++) {
|
||||
|
|
|
@ -538,7 +538,7 @@ void layer_adjustments_to_modifiers(Main &bmain,
|
|||
STRNCPY(tmd->influence.layer_name, gpl->info);
|
||||
|
||||
char modifier_name[64];
|
||||
BLI_snprintf(modifier_name, 64, "Tint %s", gpl->info);
|
||||
SNPRINTF(modifier_name, "Tint %s", gpl->info);
|
||||
STRNCPY(md->name, modifier_name);
|
||||
BKE_modifier_unique_name(&dst_object.modifiers, md);
|
||||
|
||||
|
@ -558,7 +558,7 @@ void layer_adjustments_to_modifiers(Main &bmain,
|
|||
auto *md = reinterpret_cast<NodesModifierData *>(BKE_modifier_new(eModifierType_Nodes));
|
||||
|
||||
char modifier_name[64];
|
||||
BLI_snprintf(modifier_name, 64, "Thickness %s", gpl->info);
|
||||
SNPRINTF(modifier_name, "Thickness %s", gpl->info);
|
||||
STRNCPY(md->modifier.name, modifier_name);
|
||||
BKE_modifier_unique_name(&dst_object.modifiers, &md->modifier);
|
||||
md->node_group = offset_radius_node_tree;
|
||||
|
|
|
@ -363,4 +363,96 @@ TEST(greasepencil, remove_frame_fixed_duration_overwrite_end)
|
|||
EXPECT_TRUE(layer.frames().lookup(5).is_null());
|
||||
}
|
||||
|
||||
TEST(greasepencil, remove_drawings_no_change)
|
||||
{
|
||||
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(
|
||||
BKE_id_new_nomain(ID_GP, "Grease Pencil test"));
|
||||
|
||||
grease_pencil->add_empty_drawings(3);
|
||||
|
||||
Layer &layer_a = grease_pencil->add_layer("LayerA");
|
||||
Layer &layer_b = grease_pencil->add_layer("LayerB");
|
||||
layer_b.add_frame(10, 0);
|
||||
layer_b.add_frame(20, 1);
|
||||
layer_b.add_frame(30, 2);
|
||||
|
||||
EXPECT_EQ(layer_a.frames().size(), 0);
|
||||
EXPECT_EQ(layer_b.frames().size(), 3);
|
||||
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 0);
|
||||
EXPECT_EQ(layer_b.frames().lookup(20).drawing_index, 1);
|
||||
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 2);
|
||||
/* Test DNA storage data too. */
|
||||
layer_a.prepare_for_dna_write();
|
||||
layer_b.prepare_for_dna_write();
|
||||
EXPECT_EQ(layer_a.frames_storage.num, 0);
|
||||
EXPECT_EQ(layer_b.frames_storage.num, 3);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 0);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 1);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[2].drawing_index, 2);
|
||||
|
||||
grease_pencil->remove_layer(layer_a);
|
||||
EXPECT_EQ(layer_b.frames().size(), 3);
|
||||
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 0);
|
||||
EXPECT_EQ(layer_b.frames().lookup(20).drawing_index, 1);
|
||||
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 2);
|
||||
/* Test DNA storage data too. */
|
||||
layer_b.prepare_for_dna_write();
|
||||
EXPECT_EQ(layer_b.frames_storage.num, 3);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 0);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 1);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[2].drawing_index, 2);
|
||||
|
||||
BKE_id_free(nullptr, grease_pencil);
|
||||
}
|
||||
|
||||
TEST(greasepencil, remove_drawings_with_no_users)
|
||||
{
|
||||
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(
|
||||
BKE_id_new_nomain(ID_GP, "Grease Pencil test"));
|
||||
|
||||
/* Test drawing index correctness: Removing users from drawings should remove those drawings, and
|
||||
* all index references should get updated to match the changed drawing indices. */
|
||||
|
||||
grease_pencil->add_empty_drawings(5);
|
||||
|
||||
Layer &layer_a = grease_pencil->add_layer("LayerA");
|
||||
layer_a.add_frame(10, 0);
|
||||
layer_a.add_frame(20, 1);
|
||||
layer_a.add_frame(30, 2);
|
||||
Layer &layer_b = grease_pencil->add_layer("LayerB");
|
||||
layer_b.add_frame(10, 3);
|
||||
layer_b.add_frame(30, 4);
|
||||
|
||||
EXPECT_EQ(layer_a.frames().size(), 3);
|
||||
EXPECT_EQ(layer_a.frames().lookup(10).drawing_index, 0);
|
||||
EXPECT_EQ(layer_a.frames().lookup(20).drawing_index, 1);
|
||||
EXPECT_EQ(layer_a.frames().lookup(30).drawing_index, 2);
|
||||
EXPECT_EQ(layer_b.frames().size(), 2);
|
||||
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 3);
|
||||
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 4);
|
||||
/* Test DNA storage data too. */
|
||||
layer_a.prepare_for_dna_write();
|
||||
layer_b.prepare_for_dna_write();
|
||||
EXPECT_EQ(layer_a.frames_storage.num, 3);
|
||||
EXPECT_EQ(layer_a.frames_storage.values[0].drawing_index, 0);
|
||||
EXPECT_EQ(layer_a.frames_storage.values[1].drawing_index, 1);
|
||||
EXPECT_EQ(layer_a.frames_storage.values[2].drawing_index, 2);
|
||||
EXPECT_EQ(layer_b.frames_storage.num, 2);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 3);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 4);
|
||||
|
||||
/* Drawings 0,1,2 get removed, drawings 3,4 move up (order changes). */
|
||||
grease_pencil->remove_layer(layer_a);
|
||||
EXPECT_EQ(layer_b.frames().size(), 2);
|
||||
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 1);
|
||||
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 0);
|
||||
/* Test DNA storage data too. */
|
||||
layer_b.prepare_for_dna_write();
|
||||
EXPECT_EQ(layer_b.frames_storage.num, 2);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 1);
|
||||
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 0);
|
||||
|
||||
BKE_id_free(nullptr, grease_pencil);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::greasepencil::tests
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "DNA_anim_types.h"
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_linklist_stack.h"
|
||||
#include "BLI_listbase.h"
|
||||
|
@ -600,7 +601,7 @@ void BKE_library_ID_test_usages(Main *bmain,
|
|||
* user feedback ('what would be the amounts of IDs detected as unused if this option was
|
||||
* enabled').
|
||||
*/
|
||||
struct UnusedIdsData {
|
||||
struct UnusedIDsData {
|
||||
Main *bmain;
|
||||
|
||||
const int id_tag;
|
||||
|
@ -609,12 +610,26 @@ struct UnusedIdsData {
|
|||
bool do_linked_ids;
|
||||
bool do_recursive;
|
||||
|
||||
blender::FunctionRef<bool(ID *id)> filter_fn;
|
||||
|
||||
std::array<int, INDEX_ID_MAX> *num_total;
|
||||
std::array<int, INDEX_ID_MAX> *num_local;
|
||||
std::array<int, INDEX_ID_MAX> *num_linked;
|
||||
|
||||
blender::Set<ID *> unused_ids{};
|
||||
|
||||
UnusedIDsData(Main *bmain, const int id_tag, LibQueryUnusedIDsData ¶meters)
|
||||
: bmain(bmain),
|
||||
id_tag(id_tag),
|
||||
do_local_ids(parameters.do_local_ids),
|
||||
do_linked_ids(parameters.do_linked_ids),
|
||||
do_recursive(parameters.do_recursive),
|
||||
num_total(¶meters.num_total),
|
||||
num_local(¶meters.num_local),
|
||||
num_linked(¶meters.num_linked)
|
||||
{
|
||||
}
|
||||
|
||||
void reset(const bool do_local_ids,
|
||||
const bool do_linked_ids,
|
||||
const bool do_recursive,
|
||||
|
@ -632,8 +647,11 @@ struct UnusedIdsData {
|
|||
}
|
||||
};
|
||||
|
||||
static void lib_query_unused_ids_tag_id(ID *id, UnusedIdsData &data)
|
||||
static void lib_query_unused_ids_tag_id(ID *id, UnusedIDsData &data)
|
||||
{
|
||||
if (data.filter_fn && !data.filter_fn(id)) {
|
||||
return;
|
||||
}
|
||||
id->tag |= data.id_tag;
|
||||
data.unused_ids.add(id);
|
||||
|
||||
|
@ -652,7 +670,7 @@ static void lib_query_unused_ids_tag_id(ID *id, UnusedIdsData &data)
|
|||
|
||||
/* Returns `true` if given ID is detected as part of at least one dependency loop, false otherwise.
|
||||
*/
|
||||
static bool lib_query_unused_ids_tag_recurse(ID *id, UnusedIdsData &data)
|
||||
static bool lib_query_unused_ids_tag_recurse(ID *id, UnusedIDsData &data)
|
||||
{
|
||||
/* We should never deal with embedded, not-in-main IDs here. */
|
||||
BLI_assert((id->flag & LIB_EMBEDDED_DATA) == 0);
|
||||
|
@ -764,7 +782,7 @@ static bool lib_query_unused_ids_tag_recurse(ID *id, UnusedIdsData &data)
|
|||
return is_part_of_dependency_loop;
|
||||
}
|
||||
|
||||
static void lib_query_unused_ids_tag(UnusedIdsData &data)
|
||||
static void lib_query_unused_ids_tag(UnusedIDsData &data)
|
||||
{
|
||||
BKE_main_relations_tag_set(data.bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
|
||||
|
||||
|
@ -851,15 +869,14 @@ void BKE_lib_query_unused_ids_amounts(Main *bmain, LibQueryUnusedIDsData ¶me
|
|||
* this call.
|
||||
*/
|
||||
|
||||
UnusedIdsData data{
|
||||
bmain,
|
||||
0,
|
||||
true,
|
||||
parameters.do_linked_ids,
|
||||
parameters.do_recursive,
|
||||
parameters.do_local_ids ? ¶meters.num_total : &num_dummy,
|
||||
¶meters.num_local,
|
||||
(parameters.do_local_ids && parameters.do_linked_ids) ? ¶meters.num_linked : &num_dummy};
|
||||
UnusedIDsData data(bmain, 0, parameters);
|
||||
data.do_local_ids = true;
|
||||
if (!parameters.do_local_ids) {
|
||||
data.num_total = &num_dummy;
|
||||
}
|
||||
if (!(parameters.do_local_ids && parameters.do_linked_ids)) {
|
||||
data.num_linked = &num_dummy;
|
||||
}
|
||||
lib_query_unused_ids_tag(data);
|
||||
|
||||
if (!(parameters.do_local_ids && parameters.do_linked_ids)) {
|
||||
|
@ -887,14 +904,7 @@ void BKE_lib_query_unused_ids_tag(Main *bmain, const int tag, LibQueryUnusedIDsD
|
|||
parameters.num_local.fill(0);
|
||||
parameters.num_linked.fill(0);
|
||||
|
||||
UnusedIdsData data{bmain,
|
||||
tag,
|
||||
parameters.do_local_ids,
|
||||
parameters.do_linked_ids,
|
||||
parameters.do_recursive,
|
||||
¶meters.num_total,
|
||||
¶meters.num_local,
|
||||
¶meters.num_linked};
|
||||
UnusedIDsData data(bmain, tag, parameters);
|
||||
|
||||
if (parameters.do_recursive) {
|
||||
BKE_main_relations_create(bmain, 0);
|
||||
|
|
|
@ -193,6 +193,22 @@ void BKE_preferences_extension_repo_remove(UserDef *userdef, bUserExtensionRepo
|
|||
BLI_freelinkN(&userdef->extension_repos, repo);
|
||||
}
|
||||
|
||||
bUserExtensionRepo *BKE_preferences_extension_repo_add_default(UserDef *userdef)
|
||||
{
|
||||
bUserExtensionRepo *repo = BKE_preferences_extension_repo_add(
|
||||
userdef, "Blender Official", "blender_official", "");
|
||||
STRNCPY(repo->remote_path, "https://extensions.blender.org");
|
||||
repo->flag |= USER_EXTENSION_REPO_FLAG_USE_REMOTE_PATH;
|
||||
return repo;
|
||||
}
|
||||
|
||||
bUserExtensionRepo *BKE_preferences_extension_repo_add_default_user(UserDef *userdef)
|
||||
{
|
||||
bUserExtensionRepo *repo = BKE_preferences_extension_repo_add(
|
||||
userdef, "User Default", "user_default", "");
|
||||
return repo;
|
||||
}
|
||||
|
||||
void BKE_preferences_extension_repo_name_set(UserDef *userdef,
|
||||
bUserExtensionRepo *repo,
|
||||
const char *name)
|
||||
|
|
|
@ -338,7 +338,7 @@ static const char **get_file_extensions(int format)
|
|||
}
|
||||
|
||||
/* Write a frame to the output file */
|
||||
static int write_video_frame(FFMpegContext *context, AVFrame *frame, ReportList *reports)
|
||||
static bool write_video_frame(FFMpegContext *context, AVFrame *frame, ReportList *reports)
|
||||
{
|
||||
int ret, success = 1;
|
||||
AVPacket *packet = av_packet_alloc();
|
||||
|
@ -394,8 +394,14 @@ static int write_video_frame(FFMpegContext *context, AVFrame *frame, ReportList
|
|||
}
|
||||
|
||||
/* read and encode a frame of video from the buffer */
|
||||
static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels)
|
||||
static AVFrame *generate_video_frame(FFMpegContext *context, const ImBuf *image)
|
||||
{
|
||||
/* For now only 8-bit/channel images are supported. */
|
||||
const uint8_t *pixels = image->byte_buffer.data;
|
||||
if (pixels == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AVCodecParameters *codec = context->video_stream->codecpar;
|
||||
int height = codec->height;
|
||||
AVFrame *rgb_frame;
|
||||
|
@ -1273,12 +1279,12 @@ static void ffmpeg_add_metadata_callback(void *data,
|
|||
av_dict_set(metadata, propname, propvalue, 0);
|
||||
}
|
||||
|
||||
static int start_ffmpeg_impl(FFMpegContext *context,
|
||||
RenderData *rd,
|
||||
int rectx,
|
||||
int recty,
|
||||
const char *suffix,
|
||||
ReportList *reports)
|
||||
static bool start_ffmpeg_impl(FFMpegContext *context,
|
||||
RenderData *rd,
|
||||
int rectx,
|
||||
int recty,
|
||||
const char *suffix,
|
||||
ReportList *reports)
|
||||
{
|
||||
/* Handle to the output file */
|
||||
AVFormatContext *of;
|
||||
|
@ -1324,19 +1330,19 @@ static int start_ffmpeg_impl(FFMpegContext *context,
|
|||
exts = get_file_extensions(context->ffmpeg_type);
|
||||
if (!exts) {
|
||||
BKE_report(reports, RPT_ERROR, "No valid formats found");
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt = av_guess_format(nullptr, exts[0], nullptr);
|
||||
if (!fmt) {
|
||||
BKE_report(reports, RPT_ERROR, "No valid formats found");
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
of = avformat_alloc_context();
|
||||
if (!of) {
|
||||
BKE_report(reports, RPT_ERROR, "Can't allocate FFmpeg format context");
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum AVCodecID audio_codec = context->ffmpeg_audio_codec;
|
||||
|
@ -1469,7 +1475,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
|
|||
context->outfile = of;
|
||||
av_dump_format(of, 0, filepath, 1);
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
if (of->pb) {
|
||||
|
@ -1485,7 +1491,7 @@ fail:
|
|||
}
|
||||
|
||||
avformat_free_context(of);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1626,23 +1632,22 @@ void BKE_ffmpeg_filepath_get(char filepath[/*FILE_MAX*/ 1024],
|
|||
ffmpeg_filepath_get(nullptr, filepath, rd, preview, suffix);
|
||||
}
|
||||
|
||||
int BKE_ffmpeg_start(void *context_v,
|
||||
const Scene *scene,
|
||||
RenderData *rd,
|
||||
int rectx,
|
||||
int recty,
|
||||
ReportList *reports,
|
||||
bool preview,
|
||||
const char *suffix)
|
||||
bool BKE_ffmpeg_start(void *context_v,
|
||||
const Scene *scene,
|
||||
RenderData *rd,
|
||||
int rectx,
|
||||
int recty,
|
||||
ReportList *reports,
|
||||
bool preview,
|
||||
const char *suffix)
|
||||
{
|
||||
int success;
|
||||
FFMpegContext *context = static_cast<FFMpegContext *>(context_v);
|
||||
|
||||
context->ffmpeg_autosplit_count = 0;
|
||||
context->ffmpeg_preview = preview;
|
||||
context->stamp_data = BKE_stamp_info_from_scene_static(scene);
|
||||
|
||||
success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
|
||||
bool success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
|
||||
# ifdef WITH_AUDASPACE
|
||||
if (context->audio_stream) {
|
||||
AVCodecContext *c = context->audio_codec;
|
||||
|
@ -1699,24 +1704,22 @@ static void write_audio_frames(FFMpegContext *context, double to_pts)
|
|||
}
|
||||
# endif
|
||||
|
||||
int BKE_ffmpeg_append(void *context_v,
|
||||
RenderData *rd,
|
||||
int start_frame,
|
||||
int frame,
|
||||
int *pixels,
|
||||
int rectx,
|
||||
int recty,
|
||||
const char *suffix,
|
||||
ReportList *reports)
|
||||
bool BKE_ffmpeg_append(void *context_v,
|
||||
RenderData *rd,
|
||||
int start_frame,
|
||||
int frame,
|
||||
const ImBuf *image,
|
||||
const char *suffix,
|
||||
ReportList *reports)
|
||||
{
|
||||
FFMpegContext *context = static_cast<FFMpegContext *>(context_v);
|
||||
AVFrame *avframe;
|
||||
int success = 1;
|
||||
bool success = true;
|
||||
|
||||
PRINT("Writing frame %i, render width=%d, render height=%d\n", frame, rectx, recty);
|
||||
PRINT("Writing frame %i, render width=%d, render height=%d\n", frame, image->x, image->y);
|
||||
|
||||
if (context->video_stream) {
|
||||
avframe = generate_video_frame(context, (uchar *)pixels);
|
||||
avframe = generate_video_frame(context, image);
|
||||
success = (avframe && write_video_frame(context, avframe, reports));
|
||||
# ifdef WITH_AUDASPACE
|
||||
/* Add +1 frame because we want to encode audio up until the next video frame. */
|
||||
|
@ -1731,7 +1734,7 @@ int BKE_ffmpeg_append(void *context_v,
|
|||
end_ffmpeg_impl(context, true);
|
||||
context->ffmpeg_autosplit_count++;
|
||||
|
||||
success &= start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
|
||||
success &= start_ffmpeg_impl(context, rd, image->x, image->y, suffix, reports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,31 +23,29 @@
|
|||
|
||||
#include "BKE_writemovie.hh"
|
||||
|
||||
static int start_stub(void * /*context_v*/,
|
||||
const Scene * /*scene*/,
|
||||
RenderData * /*rd*/,
|
||||
int /*rectx*/,
|
||||
int /*recty*/,
|
||||
ReportList * /*reports*/,
|
||||
bool /*preview*/,
|
||||
const char * /*suffix*/)
|
||||
static bool start_stub(void * /*context_v*/,
|
||||
const Scene * /*scene*/,
|
||||
RenderData * /*rd*/,
|
||||
int /*rectx*/,
|
||||
int /*recty*/,
|
||||
ReportList * /*reports*/,
|
||||
bool /*preview*/,
|
||||
const char * /*suffix*/)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void end_stub(void * /*context_v*/) {}
|
||||
|
||||
static int append_stub(void * /*context_v*/,
|
||||
RenderData * /*rd*/,
|
||||
int /*start_frame*/,
|
||||
int /*frame*/,
|
||||
int * /*pixels*/,
|
||||
int /*rectx*/,
|
||||
int /*recty*/,
|
||||
const char * /*suffix*/,
|
||||
ReportList * /*reports*/)
|
||||
static bool append_stub(void * /*context_v*/,
|
||||
RenderData * /*rd*/,
|
||||
int /*start_frame*/,
|
||||
int /*frame*/,
|
||||
const ImBuf * /*image*/,
|
||||
const char * /*suffix*/,
|
||||
ReportList * /*reports*/)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void *context_create_stub()
|
||||
|
|
|
@ -105,6 +105,8 @@ class StringRefBase {
|
|||
|
||||
/**
|
||||
* References a null-terminated const char array.
|
||||
*
|
||||
* StringRefNull can be compared with StringRef and StringRefNull.
|
||||
*/
|
||||
class StringRefNull : public StringRefBase {
|
||||
|
||||
|
@ -120,6 +122,8 @@ class StringRefNull : public StringRefBase {
|
|||
|
||||
/**
|
||||
* References a const char array. It might not be null terminated.
|
||||
*
|
||||
* StringRef can be compared with StringRef and StringRefNull.
|
||||
*/
|
||||
class StringRef : public StringRefBase {
|
||||
public:
|
||||
|
@ -588,7 +592,10 @@ inline std::string operator+(StringRef a, StringRef b)
|
|||
/* This does not compare StringRef and std::string_view, because of ambiguous overloads. This is
|
||||
* not a problem when std::string_view is only used at api boundaries. To compare a StringRef and a
|
||||
* std::string_view, one should convert the std::string_view to StringRef (which is very cheap).
|
||||
* Ideally, we only use StringRef in our code to avoid this problem altogether. */
|
||||
* Ideally, we only use StringRef in our code to avoid this problem altogether.
|
||||
*
|
||||
* NOTE: these functions are also suitable for StringRefNull comparisons, as these are
|
||||
* implicitly converted to StringRef by the compiler. */
|
||||
constexpr bool operator==(StringRef a, StringRef b)
|
||||
{
|
||||
return std::string_view(a) == std::string_view(b);
|
||||
|
|
|
@ -34,6 +34,71 @@ TEST(string_ref_null, CStringLengthConstructor)
|
|||
EXPECT_EQ(ref.data(), str);
|
||||
}
|
||||
|
||||
TEST(string_ref_null, CompareEqual)
|
||||
{
|
||||
StringRefNull ref1("test");
|
||||
StringRefNull ref2("test");
|
||||
StringRefNull ref3("other");
|
||||
EXPECT_TRUE(ref1 == ref2);
|
||||
EXPECT_FALSE(ref1 == ref3);
|
||||
EXPECT_TRUE(ref1 != ref3);
|
||||
EXPECT_FALSE(ref1 != ref2);
|
||||
}
|
||||
|
||||
TEST(string_ref_null, CompareEqualCharPtr1)
|
||||
{
|
||||
StringRefNull ref("test");
|
||||
EXPECT_TRUE(ref == "test");
|
||||
EXPECT_FALSE(ref == "other");
|
||||
EXPECT_TRUE(ref != "other");
|
||||
EXPECT_FALSE(ref != "test");
|
||||
}
|
||||
|
||||
TEST(string_ref_null, CompareEqualCharPtr2)
|
||||
{
|
||||
StringRefNull ref("test");
|
||||
EXPECT_TRUE("test" == ref);
|
||||
EXPECT_FALSE("other" == ref);
|
||||
EXPECT_TRUE(ref != "other");
|
||||
EXPECT_FALSE(ref != "test");
|
||||
}
|
||||
|
||||
TEST(string_ref_null, CompareEqualString1)
|
||||
{
|
||||
StringRefNull ref("test");
|
||||
EXPECT_TRUE(ref == std::string("test"));
|
||||
EXPECT_FALSE(ref == std::string("other"));
|
||||
EXPECT_TRUE(ref != std::string("other"));
|
||||
EXPECT_FALSE(ref != std::string("test"));
|
||||
}
|
||||
|
||||
TEST(string_ref_null, CompareEqualString2)
|
||||
{
|
||||
StringRefNull ref("test");
|
||||
EXPECT_TRUE(std::string("test") == ref);
|
||||
EXPECT_FALSE(std::string("other") == ref);
|
||||
EXPECT_TRUE(std::string("other") != ref);
|
||||
EXPECT_FALSE(std::string("test") != ref);
|
||||
}
|
||||
|
||||
TEST(string_ref_null, CompareEqualStringRef1)
|
||||
{
|
||||
StringRefNull ref("test");
|
||||
EXPECT_TRUE(ref == StringRef("test"));
|
||||
EXPECT_FALSE(ref == StringRef("other"));
|
||||
EXPECT_TRUE(ref != StringRef("other"));
|
||||
EXPECT_FALSE(ref != StringRef("test"));
|
||||
}
|
||||
|
||||
TEST(string_ref_null, CompareEqualStringRef2)
|
||||
{
|
||||
StringRefNull ref("test");
|
||||
EXPECT_TRUE(StringRef("test") == ref);
|
||||
EXPECT_FALSE(StringRef("other") == ref);
|
||||
EXPECT_TRUE(StringRef("other") != ref);
|
||||
EXPECT_FALSE(StringRef("test") != ref);
|
||||
}
|
||||
|
||||
TEST(string_ref, DefaultConstructor)
|
||||
{
|
||||
StringRef ref;
|
||||
|
|
|
@ -133,3 +133,7 @@ if(WITH_GTESTS)
|
|||
)
|
||||
blender_add_test_suite_lib(blenloader "${TEST_SRC}" "${INC}" "${INC_SYS}" "${TEST_LIB}")
|
||||
endif()
|
||||
|
||||
if(WITH_EXPERIMENTAL_FEATURES)
|
||||
add_definitions(-DWITH_EXPERIMENTAL_FEATURES)
|
||||
endif()
|
|
@ -923,6 +923,13 @@ void blo_do_versions_userdef(UserDef *userdef)
|
|||
}
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(402, 6)) {
|
||||
if (BLI_listbase_is_empty(&userdef->extension_repos)) {
|
||||
BKE_preferences_extension_repo_add_default(userdef);
|
||||
BKE_preferences_extension_repo_add_default_user(userdef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a USER_VERSION_ATLEAST check.
|
||||
|
@ -946,10 +953,11 @@ void BLO_sanitize_experimental_features_userpref_blend(UserDef *userdef)
|
|||
*
|
||||
* At that time master already has its version bumped so its user preferences
|
||||
* are not touched by these settings. */
|
||||
|
||||
#ifdef WITH_EXPERIMENTAL_FEATURES
|
||||
if (BKE_blender_version_is_alpha()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
MEMSET_STRUCT_AFTER(&userdef->experimental, 0, SANITIZE_AFTER_HERE);
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ class DenoiseFilter {
|
|||
DenoiseBaseOperation::DenoiseBaseOperation()
|
||||
{
|
||||
flags_.is_fullframe_operation = true;
|
||||
flags_.can_be_constant = true;
|
||||
output_rendered_ = false;
|
||||
}
|
||||
|
||||
|
@ -203,27 +204,23 @@ void DenoiseOperation::generate_denoise(MemoryBuffer *output,
|
|||
MemoryBuffer *input_albedo,
|
||||
const NodeDenoise *settings)
|
||||
{
|
||||
BLI_assert(input_color->get_buffer());
|
||||
if (!input_color->get_buffer()) {
|
||||
if (input_color->is_a_single_elem()) {
|
||||
output->fill(output->get_rect(), input_color->get_elem(0, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(COM_is_denoise_supported());
|
||||
/* OpenImageDenoise needs full buffers. */
|
||||
MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : input_color;
|
||||
MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ?
|
||||
input_normal->inflate() :
|
||||
input_normal;
|
||||
MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ?
|
||||
input_albedo->inflate() :
|
||||
input_albedo;
|
||||
|
||||
DenoiseFilter filter;
|
||||
filter.init_and_lock_denoiser(this, output);
|
||||
|
||||
filter.set_image("color", buf_color);
|
||||
filter.set_image("normal", buf_normal);
|
||||
filter.set_image("albedo", buf_albedo);
|
||||
filter.set_image("color", input_color);
|
||||
if (!input_albedo->is_a_single_elem()) {
|
||||
filter.set_image("albedo", input_albedo);
|
||||
if (!input_normal->is_a_single_elem()) {
|
||||
filter.set_image("normal", input_normal);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(settings);
|
||||
if (settings) {
|
||||
|
@ -237,17 +234,6 @@ void DenoiseOperation::generate_denoise(MemoryBuffer *output,
|
|||
|
||||
/* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */
|
||||
output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3);
|
||||
|
||||
/* Delete inflated buffers. */
|
||||
if (input_color->is_a_single_elem()) {
|
||||
delete buf_color;
|
||||
}
|
||||
if (input_normal && input_normal->is_a_single_elem()) {
|
||||
delete buf_normal;
|
||||
}
|
||||
if (input_albedo && input_albedo->is_a_single_elem()) {
|
||||
delete buf_albedo;
|
||||
}
|
||||
}
|
||||
|
||||
void DenoiseOperation::update_memory_buffer(MemoryBuffer *output,
|
||||
|
@ -286,21 +272,18 @@ MemoryBuffer *DenoisePrefilterOperation::create_memory_buffer(rcti *rect2)
|
|||
|
||||
void DenoisePrefilterOperation::generate_denoise(MemoryBuffer *output, MemoryBuffer *input)
|
||||
{
|
||||
BLI_assert(COM_is_denoise_supported());
|
||||
if (input->is_a_single_elem()) {
|
||||
copy_v4_v4(output->get_elem(0, 0), input->get_elem(0, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Denoising needs full buffers. */
|
||||
MemoryBuffer *input_buf = input->is_a_single_elem() ? input->inflate() : input;
|
||||
BLI_assert(COM_is_denoise_supported());
|
||||
|
||||
DenoiseFilter filter;
|
||||
filter.init_and_lock_denoiser(this, output);
|
||||
filter.set_image(image_name_, input_buf);
|
||||
filter.set_image(image_name_, input);
|
||||
filter.execute();
|
||||
filter.deinit_and_unlock_denoiser();
|
||||
|
||||
/* Delete inflated buffers. */
|
||||
if (input->is_a_single_elem()) {
|
||||
delete input_buf;
|
||||
}
|
||||
}
|
||||
|
||||
void DenoisePrefilterOperation::update_memory_buffer(MemoryBuffer *output,
|
||||
|
|
|
@ -81,59 +81,29 @@ void GaussianBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *outpu
|
|||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const int2 unit_offset = dimension_ == eDimension::X ? int2(1, 0) : int2(0, 1);
|
||||
MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
|
||||
const rcti &input_rect = input->get_rect();
|
||||
BuffersIterator<float> it = output->iterate_with({input}, area);
|
||||
|
||||
int min_input_coord = -1;
|
||||
int max_input_coord = -1;
|
||||
int elem_stride = -1;
|
||||
std::function<int()> get_current_coord;
|
||||
switch (dimension_) {
|
||||
case eDimension::X:
|
||||
min_input_coord = input_rect.xmin;
|
||||
max_input_coord = input_rect.xmax;
|
||||
elem_stride = input->elem_stride;
|
||||
get_current_coord = [&] { return it.x; };
|
||||
break;
|
||||
case eDimension::Y:
|
||||
min_input_coord = input_rect.ymin;
|
||||
max_input_coord = input_rect.ymax;
|
||||
elem_stride = input->row_stride;
|
||||
get_current_coord = [&] { return it.y; };
|
||||
break;
|
||||
}
|
||||
|
||||
for (; !it.is_end(); ++it) {
|
||||
const int coord = get_current_coord();
|
||||
const int coord_min = max_ii(coord - filtersize_, min_input_coord);
|
||||
const int coord_max = min_ii(coord + filtersize_ + 1, max_input_coord);
|
||||
|
||||
float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
float multiplier_accum = 0.0f;
|
||||
|
||||
const int step = QualityStepHelper::get_step();
|
||||
const float *in = it.in(0) + (intptr_t(coord_min) - coord) * elem_stride;
|
||||
const int in_stride = elem_stride * step;
|
||||
int gauss_idx = (coord_min - coord) + filtersize_;
|
||||
const int gauss_end = gauss_idx + (coord_max - coord_min);
|
||||
for (BuffersIterator<float> it = output->iterate_with({input}, area); !it.is_end(); ++it) {
|
||||
alignas(16) float4 accumulated_color = float4(0.0f);
|
||||
#if BLI_HAVE_SSE2
|
||||
__m128 accum_r = _mm_load_ps(color_accum);
|
||||
for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) {
|
||||
__m128 reg_a = _mm_load_ps(in);
|
||||
reg_a = _mm_mul_ps(reg_a, gausstab_sse_[gauss_idx]);
|
||||
accum_r = _mm_add_ps(accum_r, reg_a);
|
||||
multiplier_accum += gausstab_[gauss_idx];
|
||||
__m128 accumulated_color_sse = _mm_setzero_ps();
|
||||
for (int i = -filtersize_; i <= filtersize_; i++) {
|
||||
const int2 offset = unit_offset * i;
|
||||
__m128 weight = gausstab_sse_[i + filtersize_];
|
||||
__m128 color = _mm_load_ps(input->get_elem_clamped(it.x + offset.x, it.y + offset.y));
|
||||
__m128 weighted_color = _mm_mul_ps(color, weight);
|
||||
accumulated_color_sse = _mm_add_ps(accumulated_color_sse, weighted_color);
|
||||
}
|
||||
_mm_store_ps(color_accum, accum_r);
|
||||
_mm_store_ps(accumulated_color, accumulated_color_sse);
|
||||
#else
|
||||
for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) {
|
||||
const float multiplier = gausstab_[gauss_idx];
|
||||
madd_v4_v4fl(color_accum, in, multiplier);
|
||||
multiplier_accum += multiplier;
|
||||
for (int i = -filtersize_; i <= filtersize_; i++) {
|
||||
const int2 offset = unit_offset * i;
|
||||
const float weight = gausstab_[i + filtersize_];
|
||||
const float4 color = input->get_elem_clamped(it.x + offset.x, it.y + offset.y);
|
||||
accumulated_color += color * weight;
|
||||
}
|
||||
#endif
|
||||
mul_v4_v4fl(it.out, color_accum, 1.0f / multiplier_accum);
|
||||
copy_v4_v4(it.out, accumulated_color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,16 +32,19 @@ void HueSaturationValueCorrectOperation::execute_pixel_sampled(float output[4],
|
|||
|
||||
input_program_->read_sampled(hsv, x, y, sampler);
|
||||
|
||||
/* We parametrize the curve using the hue value. */
|
||||
const float parameter = hsv[0];
|
||||
|
||||
/* adjust hue, scaling returned default 0.5 up to 1 */
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 0, hsv[0]);
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 0, parameter);
|
||||
hsv[0] += f - 0.5f;
|
||||
|
||||
/* adjust saturation, scaling returned default 0.5 up to 1 */
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 1, hsv[0]);
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 1, parameter);
|
||||
hsv[1] *= (f * 2.0f);
|
||||
|
||||
/* adjust value, scaling returned default 0.5 up to 1 */
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 2, hsv[0]);
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 2, parameter);
|
||||
hsv[2] *= (f * 2.0f);
|
||||
|
||||
hsv[0] = hsv[0] - floorf(hsv[0]); /* mod 1.0 */
|
||||
|
@ -67,16 +70,19 @@ void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuff
|
|||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
copy_v4_v4(hsv, it.in(0));
|
||||
|
||||
/* We parametrize the curve using the hue value. */
|
||||
const float parameter = hsv[0];
|
||||
|
||||
/* Adjust hue, scaling returned default 0.5 up to 1. */
|
||||
float f = BKE_curvemapping_evaluateF(curve_mapping_, 0, hsv[0]);
|
||||
float f = BKE_curvemapping_evaluateF(curve_mapping_, 0, parameter);
|
||||
hsv[0] += f - 0.5f;
|
||||
|
||||
/* Adjust saturation, scaling returned default 0.5 up to 1. */
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 1, hsv[0]);
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 1, parameter);
|
||||
hsv[1] *= (f * 2.0f);
|
||||
|
||||
/* Adjust value, scaling returned default 0.5 up to 1. */
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 2, hsv[0]);
|
||||
f = BKE_curvemapping_evaluateF(curve_mapping_, 2, parameter);
|
||||
hsv[2] *= (f * 2.0f);
|
||||
|
||||
hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */
|
||||
|
|
|
@ -4,25 +4,10 @@
|
|||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
|
||||
|
||||
/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
|
||||
* parameters accordingly. */
|
||||
#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
|
||||
|
||||
/* Curve maps are stored in texture samplers, so ensure that the parameters evaluate the sampler at
|
||||
* the center of the pixels, because samplers are evaluated using linear interpolation. Given the
|
||||
* parameter in the [0, 1] range. */
|
||||
float compute_curve_map_coordinates(float parameter)
|
||||
{
|
||||
/* Curve maps have a fixed width of 257. We offset by the equivalent of half a pixel and scale
|
||||
* down such that the normalized parameter 1.0 corresponds to the center of the last pixel. */
|
||||
const float sampler_resolution = 257.0;
|
||||
float sampler_offset = 0.5 / sampler_resolution;
|
||||
float sampler_scale = 1.0 - (1.0 / sampler_resolution);
|
||||
return parameter * sampler_scale + sampler_offset;
|
||||
}
|
||||
|
||||
/* Same as compute_curve_map_coordinates but vectorized. */
|
||||
vec2 compute_curve_map_coordinates(vec2 parameters)
|
||||
vec3 compute_curve_map_coordinates(vec3 parameters)
|
||||
{
|
||||
const float sampler_resolution = 257.0;
|
||||
float sampler_offset = 0.5 / sampler_resolution;
|
||||
|
@ -41,21 +26,18 @@ void node_composite_hue_correct(float factor,
|
|||
vec4 hsv;
|
||||
rgb_to_hsv(color, hsv);
|
||||
|
||||
/* First, adjust the hue channel on its own, since corrections in the saturation and value
|
||||
* channels depends on the new value of the hue, not its original value. A curve map value of 0.5
|
||||
* means no change in hue, so adjust the value to get an identity at 0.5. Since the identity of
|
||||
* addition is 0, we subtract 0.5 (0.5 - 0.5 = 0). */
|
||||
const float hue_parameter = NORMALIZE_PARAMETER(hsv.x, minimums.x, range_dividers.x);
|
||||
float hue_coordinates = compute_curve_map_coordinates(hue_parameter);
|
||||
hsv.x += texture(curve_map, vec2(hue_coordinates, layer)).x - 0.5;
|
||||
|
||||
/* Second, adjust the saturation and value based on the new value of the hue. A curve map value
|
||||
* of 0.5 means no change in hue, so adjust the value to get an identity at 0.5. Since the
|
||||
* identity of duplication is 1, we multiply by 2 (0.5 * 2 = 1). */
|
||||
vec2 parameters = NORMALIZE_PARAMETER(hsv.x, minimums.yz, range_dividers.yz);
|
||||
vec2 coordinates = compute_curve_map_coordinates(parameters);
|
||||
hsv.y *= texture(curve_map, vec2(coordinates.x, layer)).y * 2.0;
|
||||
hsv.z *= texture(curve_map, vec2(coordinates.y, layer)).z * 2.0;
|
||||
/* First, normalize the hue value into the [0, 1] range for each of the curve maps and compute
|
||||
* the proper sampler coordinates for interpolation, then adjust each of the Hue, Saturation, and
|
||||
* Values accordingly to the following rules. A curve map value of 0.5 means no change in hue, so
|
||||
* adjust the value to get an identity at 0.5. Since the identity of addition is 0, we subtract
|
||||
* 0.5 (0.5 - 0.5 = 0). A curve map value of 0.5 means no change in saturation or value, so
|
||||
* adjust the value to get an identity at 0.5. Since the identity of multiplication is 1, we
|
||||
* multiply by 2 (0.5 * 2 = 1). */
|
||||
vec3 parameters = (hsv.xxx - minimums) * range_dividers;
|
||||
vec3 coordinates = compute_curve_map_coordinates(parameters);
|
||||
hsv.x += texture(curve_map, vec2(coordinates.x, layer)).x - 0.5;
|
||||
hsv.y *= texture(curve_map, vec2(coordinates.y, layer)).y * 2.0;
|
||||
hsv.z *= texture(curve_map, vec2(coordinates.z, layer)).z * 2.0;
|
||||
|
||||
/* Sanitize the new hue and saturation values. */
|
||||
hsv.x = fract(hsv.x);
|
||||
|
|
|
@ -123,10 +123,24 @@ vec3 lightprobe_eval(LightProbeSample samp, ClosureReflection reflection, vec3 P
|
|||
return mix(radiance_cube, radiance_sh, fac);
|
||||
}
|
||||
|
||||
/* Return the equivalent reflective roughness resulting in a similar lobe. */
|
||||
float lightprobe_refraction_roughness_remapping(float roughness, float ior)
|
||||
{
|
||||
/* This is a very rough mapping used by manually curve fitting the apparent roughness
|
||||
* (blurriness) of GGX reflections and GGX refraction.
|
||||
* A better fit is desirable if it is in the same order of complexity. */
|
||||
if (ior > 1.0) {
|
||||
return roughness * sqrt_fast(1.0 - 1.0 / ior);
|
||||
}
|
||||
else {
|
||||
return roughness * sqrt_fast(saturate(1.0 - ior)) * 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 lightprobe_refraction_dominant_dir(vec3 N, vec3 V, float ior, float roughness)
|
||||
{
|
||||
/* Reusing same thing as lightprobe_reflection_dominant_dir for now.
|
||||
* TODO(fclem): Find something better that take IOR and roughness into account. */
|
||||
/* Reusing same thing as lightprobe_reflection_dominant_dir for now with the roughness mapped to
|
||||
* reflection roughness. */
|
||||
float m = square(roughness);
|
||||
vec3 R = refract(-V, N, 1.0 / ior);
|
||||
float smoothness = 1.0 - m;
|
||||
|
@ -136,13 +150,15 @@ vec3 lightprobe_refraction_dominant_dir(vec3 N, vec3 V, float ior, float roughne
|
|||
|
||||
vec3 lightprobe_eval(LightProbeSample samp, ClosureRefraction cl, vec3 P, vec3 V)
|
||||
{
|
||||
vec3 L = lightprobe_refraction_dominant_dir(cl.N, V, cl.ior, cl.roughness);
|
||||
float effective_roughness = lightprobe_refraction_roughness_remapping(cl.roughness, cl.ior);
|
||||
|
||||
float lod = sphere_probe_roughness_to_lod(cl.roughness);
|
||||
vec3 L = lightprobe_refraction_dominant_dir(cl.N, V, cl.ior, effective_roughness);
|
||||
|
||||
float lod = sphere_probe_roughness_to_lod(effective_roughness);
|
||||
vec3 radiance_cube = lightprobe_spherical_sample_normalized_with_parallax(
|
||||
samp.spherical_id, P, L, lod, samp.volume_irradiance);
|
||||
|
||||
float fac = sphere_probe_roughness_to_mix_fac(cl.roughness);
|
||||
float fac = sphere_probe_roughness_to_mix_fac(effective_roughness);
|
||||
vec3 radiance_sh = spherical_harmonics_evaluate_lambert(L, samp.volume_irradiance);
|
||||
return mix(radiance_cube, radiance_sh, fac);
|
||||
}
|
||||
|
|
|
@ -4566,7 +4566,7 @@ static bool achannel_is_broken(const bAnimListElem *ale)
|
|||
const FCurve *fcu = static_cast<const FCurve *>(ale->data);
|
||||
|
||||
/* The channel is disabled (has a bad rna path), or it's a driver that failed to evaluate. */
|
||||
return (ale->flag & FCURVE_DISABLED) ||
|
||||
return (fcu->flag & FCURVE_DISABLED) ||
|
||||
(fcu->driver != nullptr && (fcu->driver->flag & DRIVER_FLAG_INVALID));
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "AS_asset_catalog_path.hh"
|
||||
#include "AS_asset_catalog_tree.hh"
|
||||
|
||||
struct AssetFilterSettings;
|
||||
struct AssetLibraryReference;
|
||||
struct bContext;
|
||||
|
||||
|
@ -28,6 +27,13 @@ class AssetRepresentation;
|
|||
|
||||
namespace blender::ed::asset {
|
||||
|
||||
struct AssetFilterSettings {
|
||||
/** Tags to match against. These are newly allocated, and compared against the
|
||||
* #AssetMetaData.tags. */
|
||||
ListBase tags; /* AssetTag */
|
||||
uint64_t id_types; /* rna_enum_id_type_filter_items */
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare \a asset against the settings of \a filter.
|
||||
*
|
||||
|
|
|
@ -769,7 +769,7 @@ void clear_operator_asset_trees()
|
|||
|
||||
static asset::AssetItemTree build_catalog_tree(const bContext &C, const Object &active_object)
|
||||
{
|
||||
AssetFilterSettings type_filter{};
|
||||
asset::AssetFilterSettings type_filter{};
|
||||
type_filter.id_types = FILTER_ID_NT;
|
||||
const GeometryNodeAssetTraitFlag flag = asset_flag_for_context(active_object);
|
||||
auto meta_data_filter = [&](const AssetMetaData &meta_data) {
|
||||
|
@ -785,6 +785,7 @@ static asset::AssetItemTree build_catalog_tree(const bContext &C, const Object &
|
|||
return true;
|
||||
};
|
||||
const AssetLibraryReference library = asset_system::all_library_reference();
|
||||
asset_system::all_library_reload_catalogs_if_dirty();
|
||||
return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ void set_selected_frames_type(bke::greasepencil::Layer &layer,
|
|||
for (GreasePencilFrame &frame : layer.frames_for_write().values()) {
|
||||
if (frame.is_selected()) {
|
||||
frame.type = key_type;
|
||||
layer.tag_frames_map_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +171,10 @@ bool duplicate_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::L
|
|||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
layer.tag_frames_map_changed();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -210,6 +215,7 @@ bool select_frame_at(bke::greasepencil::Layer &layer,
|
|||
return false;
|
||||
}
|
||||
select_frame(*frame, select_mode);
|
||||
layer.tag_frames_map_changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -232,6 +238,7 @@ void select_all_frames(bke::greasepencil::Layer &layer, const short select_mode)
|
|||
{
|
||||
for (auto item : layer.frames_for_write().items()) {
|
||||
select_frame(item.value, select_mode);
|
||||
layer.tag_frames_map_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,6 +276,8 @@ void select_frames_region(KeyframeEditData *ked,
|
|||
select_frame(frame, select_mode);
|
||||
}
|
||||
}
|
||||
|
||||
node.as_layer().tag_frames_map_changed();
|
||||
}
|
||||
}
|
||||
else if (node.is_group()) {
|
||||
|
@ -288,6 +297,7 @@ void select_frames_range(bke::greasepencil::TreeNode &node,
|
|||
for (auto [frame_number, frame] : node.as_layer().frames_for_write().items()) {
|
||||
if (IN_RANGE(float(frame_number), min, max)) {
|
||||
select_frame(frame, select_mode);
|
||||
node.as_layer().tag_frames_map_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,14 +234,24 @@ static Array<int> get_frame_numbers_for_layer(const bke::greasepencil::Layer &la
|
|||
const int current_frame,
|
||||
const bool use_multi_frame_editing)
|
||||
{
|
||||
Vector<int> frame_numbers({current_frame});
|
||||
Vector<int> frame_numbers;
|
||||
if (use_multi_frame_editing) {
|
||||
bool current_frame_is_covered = false;
|
||||
const int drawing_index_at_current_frame = layer.drawing_index_at(current_frame);
|
||||
for (const auto [frame_number, frame] : layer.frames().items()) {
|
||||
if (frame_number != current_frame && frame.is_selected()) {
|
||||
frame_numbers.append_unchecked(frame_number);
|
||||
if (!frame.is_selected()) {
|
||||
continue;
|
||||
}
|
||||
frame_numbers.append(frame_number);
|
||||
current_frame_is_covered |= (frame.drawing_index == drawing_index_at_current_frame);
|
||||
}
|
||||
if (current_frame_is_covered) {
|
||||
return frame_numbers.as_span();
|
||||
}
|
||||
}
|
||||
|
||||
frame_numbers.append(current_frame);
|
||||
|
||||
return frame_numbers.as_span();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
/* Struct Declarations */
|
||||
|
||||
struct ARegion;
|
||||
struct AssetFilterSettings;
|
||||
struct AutoComplete;
|
||||
struct EnumPropertyItem;
|
||||
struct FileSelectParams;
|
||||
|
@ -68,6 +67,9 @@ struct wmOperator;
|
|||
struct wmOperatorType;
|
||||
struct wmRegionListenerParams;
|
||||
struct wmWindow;
|
||||
namespace blender::ed::asset {
|
||||
struct AssetFilterSettings;
|
||||
}
|
||||
|
||||
struct uiBlock;
|
||||
struct uiBut;
|
||||
|
@ -2690,7 +2692,7 @@ void uiTemplateAssetView(uiLayout *layout,
|
|||
const char *assets_propname,
|
||||
PointerRNA *active_dataptr,
|
||||
const char *active_propname,
|
||||
const AssetFilterSettings *filter_settings,
|
||||
const blender::ed::asset::AssetFilterSettings *filter_settings,
|
||||
int display_flags,
|
||||
const char *activate_opname,
|
||||
PointerRNA *r_activate_op_properties,
|
||||
|
|
|
@ -35,7 +35,7 @@ using namespace blender::ed;
|
|||
|
||||
struct AssetViewListData {
|
||||
AssetLibraryReference asset_library_ref;
|
||||
AssetFilterSettings filter_settings;
|
||||
asset::AssetFilterSettings filter_settings;
|
||||
bScreen *screen;
|
||||
bool show_names;
|
||||
};
|
||||
|
@ -118,7 +118,7 @@ static void asset_view_filter_items(uiList *ui_list,
|
|||
const char *propname)
|
||||
{
|
||||
AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata;
|
||||
AssetFilterSettings &filter_settings = list_data->filter_settings;
|
||||
asset::AssetFilterSettings &filter_settings = list_data->filter_settings;
|
||||
|
||||
uiListNameFilter name_filter(*ui_list);
|
||||
|
||||
|
@ -219,7 +219,7 @@ void uiTemplateAssetView(uiLayout *layout,
|
|||
const char *assets_propname,
|
||||
PointerRNA *active_dataptr,
|
||||
const char *active_propname,
|
||||
const AssetFilterSettings *filter_settings,
|
||||
const asset::AssetFilterSettings *filter_settings,
|
||||
const int display_flags,
|
||||
const char *activate_opname,
|
||||
PointerRNA *r_activate_op_properties,
|
||||
|
|
|
@ -3271,6 +3271,9 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
|
|||
float *hsv = cpicker->hsv_perceptual;
|
||||
float hsv_n[3];
|
||||
|
||||
/* Is this the larger color canvas or narrow color slider? */
|
||||
bool is_canvas = ELEM(hsv_but->gradient_type, UI_GRAD_SV, UI_GRAD_HV, UI_GRAD_HS);
|
||||
|
||||
/* Initialize for compatibility. */
|
||||
copy_v3_v3(hsv_n, hsv);
|
||||
|
||||
|
@ -3292,15 +3295,15 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
|
|||
imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
|
||||
immUnbindProgram();
|
||||
|
||||
if (BLI_rcti_size_x(rect) / BLI_rcti_size_y(rect) < 3) {
|
||||
/* This is for the full square HSV cube. */
|
||||
if (is_canvas) {
|
||||
/* Round cursor in the large square area. */
|
||||
float margin = (4.0f * UI_SCALE_FAC);
|
||||
CLAMP(x, rect->xmin + margin, rect->xmax - margin);
|
||||
CLAMP(y, rect->ymin + margin, rect->ymax - margin);
|
||||
ui_hsv_cursor(x, y, zoom, rgb, hsv, but->flag & UI_SELECT);
|
||||
}
|
||||
else {
|
||||
/* This is for the narrow horizontal gradient. */
|
||||
/* Square indicator in the narrow area. */
|
||||
rctf rectf;
|
||||
BLI_rctf_rcti_copy(&rectf, rect);
|
||||
const float margin = (2.0f * UI_SCALE_FAC);
|
||||
|
|
|
@ -88,7 +88,9 @@ struct RingSelOpData {
|
|||
static void ringsel_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
|
||||
{
|
||||
RingSelOpData *lcd = static_cast<RingSelOpData *>(arg);
|
||||
EDBM_preselect_edgering_draw(lcd->presel_edgering, lcd->ob->object_to_world().ptr());
|
||||
if (lcd->ob != nullptr) {
|
||||
EDBM_preselect_edgering_draw(lcd->presel_edgering, lcd->ob->object_to_world().ptr());
|
||||
}
|
||||
}
|
||||
|
||||
static void edgering_select(RingSelOpData *lcd)
|
||||
|
|
|
@ -50,7 +50,7 @@ static bool all_loading_finished()
|
|||
|
||||
static asset::AssetItemTree build_catalog_tree(const bContext &C)
|
||||
{
|
||||
AssetFilterSettings type_filter{};
|
||||
asset::AssetFilterSettings type_filter{};
|
||||
type_filter.id_types = FILTER_ID_NT;
|
||||
auto meta_data_filter = [&](const AssetMetaData &meta_data) {
|
||||
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
|
||||
|
@ -65,6 +65,7 @@ static asset::AssetItemTree build_catalog_tree(const bContext &C)
|
|||
return true;
|
||||
};
|
||||
const AssetLibraryReference library = asset_system::all_library_reference();
|
||||
asset_system::all_library_reload_catalogs_if_dirty();
|
||||
return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "DNA_array_utils.hh"
|
||||
|
@ -199,6 +200,12 @@ static bool bake_simulation_poll(bContext *C)
|
|||
CTX_wm_operator_poll_msg_set(C, "File must be saved before baking");
|
||||
return false;
|
||||
}
|
||||
Object *ob = ED_object_active_context(C);
|
||||
const bool use_frame_cache = ob->flag & OB_FLAG_USE_SIMULATION_CACHE;
|
||||
if (!use_frame_cache) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Cache has to be enabled");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,10 +91,11 @@ using blender::Vector;
|
|||
|
||||
static CLG_LogRef LOG = {"ed.sculpt_paint"};
|
||||
|
||||
static float sculpt_calc_radius(ViewContext *vc,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const float3 location)
|
||||
namespace blender::ed::sculpt_paint {
|
||||
float sculpt_calc_radius(ViewContext *vc,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const float3 location)
|
||||
{
|
||||
if (!BKE_brush_use_locked_size(scene, brush)) {
|
||||
return paint_calc_object_space_radius(vc, location, BKE_brush_size_get(scene, brush));
|
||||
|
@ -103,6 +104,7 @@ static float sculpt_calc_radius(ViewContext *vc,
|
|||
return BKE_brush_unprojected_radius_get(scene, brush);
|
||||
}
|
||||
}
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
bool ED_sculpt_report_if_shape_key_is_locked(const Object *ob, ReportList *reports)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "DNA_brush_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
@ -42,6 +43,9 @@
|
|||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
|
@ -467,10 +471,18 @@ void SCULPT_OT_set_detail_size(wmOperatorType *ot)
|
|||
#define DETAIL_SIZE_DELTA_SPEED 0.08f
|
||||
#define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f
|
||||
|
||||
enum eDyntopoDetailingMode {
|
||||
DETAILING_MODE_RESOLUTION = 0,
|
||||
DETAILING_MODE_BRUSH_PERCENT = 1,
|
||||
DETAILING_MODE_DETAIL_SIZE = 2
|
||||
};
|
||||
|
||||
struct DyntopoDetailSizeEditCustomData {
|
||||
void *draw_handle;
|
||||
Object *active_object;
|
||||
|
||||
eDyntopoDetailingMode mode;
|
||||
|
||||
float init_mval[2];
|
||||
float accurate_mval[2];
|
||||
|
||||
|
@ -479,11 +491,19 @@ struct DyntopoDetailSizeEditCustomData {
|
|||
bool accurate_mode;
|
||||
bool sample_mode;
|
||||
|
||||
float init_detail_size;
|
||||
float accurate_detail_size;
|
||||
float detail_size;
|
||||
/* The values stored here vary based on the detailing mode. */
|
||||
float init_value;
|
||||
float accurate_value;
|
||||
float current_value;
|
||||
|
||||
float radius;
|
||||
|
||||
float brush_radius;
|
||||
float pixel_radius;
|
||||
|
||||
float min_value;
|
||||
float max_value;
|
||||
|
||||
float preview_tri[3][3];
|
||||
float gizmo_mat[4][4];
|
||||
};
|
||||
|
@ -495,9 +515,20 @@ static void dyntopo_detail_size_parallel_lines_draw(uint pos3d,
|
|||
bool flip,
|
||||
const float angle)
|
||||
{
|
||||
float object_space_constant_detail = 1.0f /
|
||||
(cd->detail_size *
|
||||
mat4_to_scale(cd->active_object->object_to_world().ptr()));
|
||||
float object_space_constant_detail;
|
||||
if (cd->mode == DETAILING_MODE_RESOLUTION) {
|
||||
object_space_constant_detail = 1.0f /
|
||||
(cd->current_value *
|
||||
mat4_to_scale(cd->active_object->object_to_world().ptr()));
|
||||
}
|
||||
else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
|
||||
object_space_constant_detail = cd->brush_radius * cd->current_value / 100.0f;
|
||||
}
|
||||
else {
|
||||
object_space_constant_detail = (cd->brush_radius / cd->pixel_radius) *
|
||||
(cd->current_value * U.pixelsize) /
|
||||
detail_size::RELATIVE_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
/* The constant detail represents the maximum edge length allowed before subdividing it. If the
|
||||
* triangle grid preview is created with this value it will represent an ideal mesh density where
|
||||
|
@ -593,6 +624,26 @@ static void dyntopo_detail_size_edit_cancel(bContext *C, wmOperator *op)
|
|||
ss->draw_faded_cursor = false;
|
||||
MEM_freeN(op->customdata);
|
||||
ED_workspace_status_text(C, nullptr);
|
||||
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
ED_area_status_text(area, nullptr);
|
||||
}
|
||||
|
||||
static void dyntopo_detail_size_bounds(DyntopoDetailSizeEditCustomData *cd)
|
||||
{
|
||||
/* TODO: Get range from RNA for these values? */
|
||||
if (cd->mode == DETAILING_MODE_RESOLUTION) {
|
||||
cd->min_value = 1.0f;
|
||||
cd->max_value = 500.0f;
|
||||
}
|
||||
else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
|
||||
cd->min_value = 0.5f;
|
||||
cd->max_value = 100.0f;
|
||||
}
|
||||
else {
|
||||
cd->min_value = 0.5f;
|
||||
cd->max_value = 40.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void dyntopo_detail_size_sample_from_surface(Object *ob,
|
||||
|
@ -616,7 +667,19 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob,
|
|||
/* Use 0.7 as the average of min and max dyntopo edge length. */
|
||||
const float detail_size = 0.7f / (avg_edge_len *
|
||||
mat4_to_scale(cd->active_object->object_to_world().ptr()));
|
||||
cd->detail_size = clamp_f(detail_size, 1.0f, 500.0f);
|
||||
float sampled_value;
|
||||
if (cd->mode == DETAILING_MODE_RESOLUTION) {
|
||||
sampled_value = detail_size;
|
||||
}
|
||||
else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
|
||||
sampled_value = detail_size::constant_to_brush_detail(
|
||||
detail_size, cd->brush_radius, cd->active_object);
|
||||
}
|
||||
else {
|
||||
sampled_value = detail_size::constant_to_relative_detail(
|
||||
detail_size, cd->brush_radius, cd->pixel_radius, cd->active_object);
|
||||
}
|
||||
cd->current_value = clamp_f(sampled_value, cd->min_value, cd->max_value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,27 +689,58 @@ static void dyntopo_detail_size_update_from_mouse_delta(DyntopoDetailSizeEditCus
|
|||
const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
|
||||
|
||||
float detail_size_delta;
|
||||
float invert = cd->mode == DETAILING_MODE_RESOLUTION ? 1.0f : -1.0f;
|
||||
if (cd->accurate_mode) {
|
||||
detail_size_delta = mval[0] - cd->accurate_mval[0];
|
||||
cd->detail_size = cd->accurate_detail_size +
|
||||
detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED;
|
||||
cd->current_value = cd->accurate_value +
|
||||
detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED * invert;
|
||||
}
|
||||
else {
|
||||
detail_size_delta = mval[0] - cd->init_mval[0];
|
||||
cd->detail_size = cd->init_detail_size + detail_size_delta * DETAIL_SIZE_DELTA_SPEED;
|
||||
cd->current_value = cd->init_value + detail_size_delta * DETAIL_SIZE_DELTA_SPEED * invert;
|
||||
}
|
||||
|
||||
if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) {
|
||||
cd->accurate_mode = true;
|
||||
copy_v2_v2(cd->accurate_mval, mval);
|
||||
cd->accurate_detail_size = cd->detail_size;
|
||||
cd->accurate_value = cd->current_value;
|
||||
}
|
||||
if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) {
|
||||
cd->accurate_mode = false;
|
||||
cd->accurate_detail_size = 0.0f;
|
||||
cd->accurate_value = 0.0f;
|
||||
}
|
||||
|
||||
cd->detail_size = clamp_f(cd->detail_size, 1.0f, 500.0f);
|
||||
cd->current_value = clamp_f(cd->current_value, cd->min_value, cd->max_value);
|
||||
}
|
||||
|
||||
static void dyntopo_detail_size_update_header(bContext *C,
|
||||
const DyntopoDetailSizeEditCustomData *cd)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
Sculpt *sd = scene->toolsettings->sculpt;
|
||||
PointerRNA sculpt_ptr = RNA_pointer_create(&scene->id, &RNA_Sculpt, sd);
|
||||
|
||||
char msg[UI_MAX_DRAW_STR];
|
||||
const char *format_string;
|
||||
const char *property_name;
|
||||
if (cd->mode == DETAILING_MODE_RESOLUTION) {
|
||||
property_name = "constant_detail_resolution";
|
||||
format_string = "%s: %0.4f";
|
||||
}
|
||||
else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
|
||||
property_name = "detail_percent";
|
||||
format_string = "%s: %3.1f%%";
|
||||
}
|
||||
else {
|
||||
property_name = "detail_size";
|
||||
format_string = "%s: %0.4f";
|
||||
}
|
||||
const PropertyRNA *prop = RNA_struct_find_property(&sculpt_ptr, property_name);
|
||||
const char *ui_name = RNA_property_ui_name(prop);
|
||||
SNPRINTF(msg, format_string, ui_name, cd->current_value);
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
ED_area_status_text(area, msg);
|
||||
}
|
||||
|
||||
static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
|
@ -673,11 +767,23 @@ static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmE
|
|||
(event->type == EVT_PADENTER && event->val == KM_PRESS))
|
||||
{
|
||||
ED_region_draw_cb_exit(region->type, cd->draw_handle);
|
||||
sd->constant_detail = cd->detail_size;
|
||||
if (cd->mode == DETAILING_MODE_RESOLUTION) {
|
||||
sd->constant_detail = cd->current_value;
|
||||
}
|
||||
else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
|
||||
sd->detail_percent = cd->current_value;
|
||||
}
|
||||
else {
|
||||
sd->detail_size = cd->current_value;
|
||||
}
|
||||
|
||||
ss->draw_faded_cursor = false;
|
||||
MEM_freeN(op->customdata);
|
||||
ED_region_tag_redraw(region);
|
||||
ED_workspace_status_text(C, nullptr);
|
||||
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
ED_area_status_text(area, nullptr);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
@ -699,23 +805,29 @@ static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmE
|
|||
}
|
||||
/* Regular mode, changes the detail size by moving the cursor. */
|
||||
dyntopo_detail_size_update_from_mouse_delta(cd, event);
|
||||
dyntopo_detail_size_update_header(C, cd);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static float dyntopo_detail_size_initial_value(const Sculpt *sd, const eDyntopoDetailingMode mode)
|
||||
{
|
||||
if (mode == DETAILING_MODE_RESOLUTION) {
|
||||
return sd->constant_detail;
|
||||
}
|
||||
else if (mode == DETAILING_MODE_BRUSH_PERCENT) {
|
||||
return sd->detail_percent;
|
||||
}
|
||||
else {
|
||||
return sd->detail_size;
|
||||
}
|
||||
}
|
||||
|
||||
static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
||||
const ToolSettings *tool_settings = CTX_data_tool_settings(C);
|
||||
Sculpt *sd = tool_settings->sculpt;
|
||||
|
||||
/* Fallback to radial control for modes other than SCULPT_DYNTOPO_DETAIL_CONSTANT [same as in
|
||||
* SCULPT_OT_set_detail_size]. */
|
||||
if (!(sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL))) {
|
||||
sculpt_detail_size_set_radial_control(C);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/* Special method for SCULPT_DYNTOPO_DETAIL_CONSTANT. */
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
Object *active_object = CTX_data_active_object(C);
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
|
@ -728,16 +840,36 @@ static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wm
|
|||
cd->active_object = active_object;
|
||||
cd->init_mval[0] = event->mval[0];
|
||||
cd->init_mval[1] = event->mval[1];
|
||||
cd->detail_size = sd->constant_detail;
|
||||
cd->init_detail_size = sd->constant_detail;
|
||||
if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) {
|
||||
cd->mode = DETAILING_MODE_RESOLUTION;
|
||||
}
|
||||
else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) {
|
||||
cd->mode = DETAILING_MODE_BRUSH_PERCENT;
|
||||
}
|
||||
else {
|
||||
cd->mode = DETAILING_MODE_DETAIL_SIZE;
|
||||
}
|
||||
|
||||
const float initial_detail_size = dyntopo_detail_size_initial_value(sd, cd->mode);
|
||||
cd->current_value = initial_detail_size;
|
||||
cd->init_value = initial_detail_size;
|
||||
copy_v4_v4(cd->outline_col, brush->add_col);
|
||||
op->customdata = cd;
|
||||
|
||||
SculptSession *ss = active_object->sculpt;
|
||||
dyntopo_detail_size_bounds(cd);
|
||||
cd->radius = ss->cursor_radius;
|
||||
|
||||
/* Generates the matrix to position the gizmo in the surface of the mesh using the same location
|
||||
* and orientation as the brush cursor. */
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
ViewContext vc = ED_view3d_viewcontext_init(C, depsgraph);
|
||||
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
cd->brush_radius = blender::ed::sculpt_paint::sculpt_calc_radius(
|
||||
&vc, brush, scene, ss->cursor_location);
|
||||
cd->pixel_radius = BKE_brush_size_get(scene, brush);
|
||||
|
||||
/* Generates the matrix to position the gizmo in the surface of the mesh using the same
|
||||
* location and orientation as the brush cursor. */
|
||||
float cursor_trans[4][4], cursor_rot[4][4];
|
||||
const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
|
||||
float quat[4];
|
||||
|
@ -775,7 +907,9 @@ static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wm
|
|||
const char *status_str = IFACE_(
|
||||
"Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel, "
|
||||
"SHIFT: precision mode, CTRL: sample detail size");
|
||||
|
||||
ED_workspace_status_text(C, status_str);
|
||||
dyntopo_detail_size_update_header(C, cd);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
@ -798,4 +932,26 @@ void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
|
|||
|
||||
} // namespace blender::ed::sculpt_paint::dyntopo
|
||||
|
||||
namespace blender::ed::sculpt_paint::dyntopo::detail_size {
|
||||
float constant_to_brush_detail(const float constant_detail,
|
||||
const float brush_radius,
|
||||
const Object *ob)
|
||||
{
|
||||
const float object_scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
|
||||
return 100.0f / (constant_detail * brush_radius * object_scale);
|
||||
}
|
||||
|
||||
float constant_to_relative_detail(const float constant_detail,
|
||||
const float brush_radius,
|
||||
const float pixel_radius,
|
||||
const Object *ob)
|
||||
{
|
||||
const float object_scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
|
||||
return (pixel_radius / brush_radius) * (RELATIVE_SCALE_FACTOR / U.pixelsize) *
|
||||
(1.0f / (constant_detail * object_scale));
|
||||
}
|
||||
} // namespace blender::ed::sculpt_paint::dyntopo::detail_size
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -1229,6 +1229,28 @@ void triangulate(BMesh *bm);
|
|||
|
||||
WarnFlag check_attribute_warning(Scene *scene, Object *ob);
|
||||
|
||||
namespace detail_size {
|
||||
constexpr float RELATIVE_SCALE_FACTOR = 0.4f;
|
||||
|
||||
/**
|
||||
* Converts from Sculpt#constant_detail to equivalent Sculpt#detail_percent value.
|
||||
*
|
||||
* Corresponds to a change from Constant & Manual Detailing to Brush Detailing.
|
||||
*/
|
||||
float constant_to_brush_detail(const float constant_detail,
|
||||
const float brush_radius,
|
||||
const Object *ob);
|
||||
|
||||
/**
|
||||
* Converts from Sculpt#constant_detail to equivalent Sculpt#detail_size value.
|
||||
*
|
||||
* Corresponds to a change from Constant & Manual Detailing to Relative Detailing.
|
||||
*/
|
||||
float constant_to_relative_detail(const float constant_detail,
|
||||
const float brush_radius,
|
||||
const float pixel_radius,
|
||||
const Object *ob);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -1901,6 +1923,13 @@ int SCULPT_vertex_island_get(const SculptSession *ss, PBVHVertRef vertex);
|
|||
|
||||
/** \} */
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
float sculpt_calc_radius(ViewContext *vc,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const float3 location);
|
||||
}
|
||||
|
||||
inline void *SCULPT_vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr)
|
||||
{
|
||||
if (attr->data) {
|
||||
|
|
|
@ -41,7 +41,7 @@ static bool all_loading_finished()
|
|||
|
||||
static asset::AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree &node_tree)
|
||||
{
|
||||
AssetFilterSettings type_filter{};
|
||||
asset::AssetFilterSettings type_filter{};
|
||||
type_filter.id_types = FILTER_ID_NT;
|
||||
auto meta_data_filter = [&](const AssetMetaData &meta_data) {
|
||||
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
|
||||
|
@ -51,6 +51,7 @@ static asset::AssetItemTree build_catalog_tree(const bContext &C, const bNodeTre
|
|||
return true;
|
||||
};
|
||||
const AssetLibraryReference library = asset_system::all_library_reference();
|
||||
asset_system::all_library_reload_catalogs_if_dirty();
|
||||
return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter);
|
||||
}
|
||||
|
||||
|
|
|
@ -233,7 +233,7 @@ static void gather_search_link_ops_for_asset_library(const bContext &C,
|
|||
const bool skip_local,
|
||||
Vector<SocketLinkOperation> &search_link_ops)
|
||||
{
|
||||
AssetFilterSettings filter_settings{};
|
||||
asset::AssetFilterSettings filter_settings{};
|
||||
filter_settings.id_types = FILTER_ID_NT;
|
||||
|
||||
asset::list::storage_fetch(&library_ref, &C);
|
||||
|
|
|
@ -2163,7 +2163,10 @@ static int unused_message_popup_width_compute(bContext *C)
|
|||
/* Computation of unused data amounts, with all options ON.
|
||||
* Used to estimate the maximum required width for the dialog. */
|
||||
Main *bmain = CTX_data_main(C);
|
||||
LibQueryUnusedIDsData data = {true, true, true, {}, {}, {}};
|
||||
LibQueryUnusedIDsData data;
|
||||
data.do_local_ids = true;
|
||||
data.do_linked_ids = true;
|
||||
data.do_recursive = true;
|
||||
BKE_lib_query_unused_ids_amounts(bmain, data);
|
||||
|
||||
std::string unused_message = "";
|
||||
|
|
|
@ -354,7 +354,7 @@ static void PREFERENCES_OT_extension_repo_add(wmOperatorType *ot)
|
|||
static const EnumPropertyItem repo_type_items[] = {
|
||||
{int(bUserExtensionRepoAddType::Remote),
|
||||
"REMOTE",
|
||||
ICON_WORLD,
|
||||
ICON_NETWORK_DRIVE,
|
||||
"Add Remote Repository",
|
||||
"Add a repository referencing an remote repository "
|
||||
"with support for listing and updating extensions"},
|
||||
|
@ -630,6 +630,33 @@ static void PREFERENCES_OT_extension_repo_upgrade(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Drop Extension Operator
|
||||
* \{ */
|
||||
|
||||
static int preferences_extension_url_drop_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
char *url = RNA_string_get_alloc(op->ptr, "url", nullptr, 0, nullptr);
|
||||
BKE_callback_exec_string(CTX_data_main(C), BKE_CB_EVT_EXTENSION_DROP_URL, url);
|
||||
MEM_freeN(url);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void PREFERENCES_OT_extension_url_drop(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Drop Extension URL";
|
||||
ot->description = "Handle dropping an extension URL";
|
||||
ot->idname = "PREFERENCES_OT_extension_url_drop";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = preferences_extension_url_drop_exec;
|
||||
|
||||
RNA_def_string(ot->srna, "url", nullptr, 0, "URL", "Location of the extension to install");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Associate File Type Operator (Windows only)
|
||||
* \{ */
|
||||
|
@ -746,6 +773,96 @@ static void PREFERENCES_OT_unassociate_blend(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Drag & Drop URL
|
||||
* \{ */
|
||||
|
||||
static bool drop_extension_url_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
|
||||
{
|
||||
if (!U.experimental.use_extension_repos) {
|
||||
return false;
|
||||
}
|
||||
if (drag->type != WM_DRAG_STRING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string &str = WM_drag_get_string(drag);
|
||||
|
||||
/* Only URL formatted text. */
|
||||
const char *cstr = str.c_str();
|
||||
if (!(STRPREFIX(cstr, "http://") || STRPREFIX(cstr, "https://") || STRPREFIX(cstr, "file://"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Only single line strings. */
|
||||
if (str.find('\n') != std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
const char *cstr_ext = BLI_path_extension(cstr);
|
||||
if (!(cstr_ext && STRCASEEQ(cstr_ext, ".zip"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void drop_extension_url_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
|
||||
{
|
||||
/* Copy drag URL to properties. */
|
||||
const std::string &str = WM_drag_get_string(drag);
|
||||
RNA_string_set(drop->ptr, "url", str.c_str());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Drag & Drop Paths
|
||||
* \{ */
|
||||
|
||||
static bool drop_extension_path_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
|
||||
{
|
||||
if (!U.experimental.use_extension_repos) {
|
||||
return false;
|
||||
}
|
||||
if (drag->type != WM_DRAG_PATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *cstr = WM_drag_get_single_path(drag);
|
||||
const char *cstr_ext = BLI_path_extension(cstr);
|
||||
if (!(cstr_ext && STRCASEEQ(cstr_ext, ".zip"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void drop_extension_path_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
|
||||
{
|
||||
/* Copy drag URL to properties. */
|
||||
const char *cstr = WM_drag_get_single_path(drag);
|
||||
RNA_string_set(drop->ptr, "url", cstr);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
static void ED_dropbox_drop_extension()
|
||||
{
|
||||
ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
WM_dropbox_add(lb,
|
||||
"PREFERENCES_OT_extension_url_drop",
|
||||
drop_extension_url_poll,
|
||||
drop_extension_url_copy,
|
||||
nullptr,
|
||||
nullptr);
|
||||
WM_dropbox_add(lb,
|
||||
"PREFERENCES_OT_extension_url_drop",
|
||||
drop_extension_path_poll,
|
||||
drop_extension_path_copy,
|
||||
nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void ED_operatortypes_userpref()
|
||||
{
|
||||
WM_operatortype_append(PREFERENCES_OT_reset_default_theme);
|
||||
|
@ -760,7 +877,10 @@ void ED_operatortypes_userpref()
|
|||
WM_operatortype_append(PREFERENCES_OT_extension_repo_remove);
|
||||
WM_operatortype_append(PREFERENCES_OT_extension_repo_sync);
|
||||
WM_operatortype_append(PREFERENCES_OT_extension_repo_upgrade);
|
||||
WM_operatortype_append(PREFERENCES_OT_extension_url_drop);
|
||||
|
||||
WM_operatortype_append(PREFERENCES_OT_associate_blend);
|
||||
WM_operatortype_append(PREFERENCES_OT_unassociate_blend);
|
||||
|
||||
ED_dropbox_drop_extension();
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
|
|||
continue;
|
||||
}
|
||||
Object *object_eval = DEG_get_evaluated_object(depsgraph, tc.obedit);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object_eval->data);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
|
||||
Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
|
||||
|
||||
int layer_points_offset = 0;
|
||||
|
|
|
@ -279,7 +279,7 @@ eSnapMode SnapData::snap_edge_points_impl(SnapObjectContext *sctx,
|
|||
if (lambda < (range) || (1.0f - range) < lambda) {
|
||||
int v_id = lambda < 0.5f ? 0 : 1;
|
||||
|
||||
if (this->snap_point(v_pair[v_id], v_id)) {
|
||||
if (this->snap_point(v_pair[v_id], vindex[v_id])) {
|
||||
elem = SCE_SNAP_TO_EDGE_ENDPOINT;
|
||||
this->copy_vert_no(vindex[v_id], this->nearest_point.no);
|
||||
}
|
||||
|
|
|
@ -596,7 +596,7 @@ void GPU_shader_constant_bool_ex(GPUShader *sh, int location, bool value)
|
|||
Shader &shader = *unwrap(sh);
|
||||
BLI_assert(shader.constants.types[location] == gpu::shader::Type::BOOL);
|
||||
shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].u,
|
||||
static_cast<uint32_t>(value));
|
||||
uint32_t(value));
|
||||
}
|
||||
|
||||
void GPU_shader_constant_int(GPUShader *sh, const char *name, int value)
|
||||
|
|
|
@ -662,6 +662,7 @@ void MTLCommandBufferManager::encode_signal_event(id<MTLEvent> event, uint64_t s
|
|||
BLI_assert(cmd_buf);
|
||||
this->end_active_command_encoder();
|
||||
[cmd_buf encodeSignalEvent:event value:signal_value];
|
||||
register_encoder_counters();
|
||||
}
|
||||
|
||||
void MTLCommandBufferManager::encode_wait_for_event(id<MTLEvent> event, uint64_t signal_value)
|
||||
|
@ -671,6 +672,7 @@ void MTLCommandBufferManager::encode_wait_for_event(id<MTLEvent> event, uint64_t
|
|||
BLI_assert(cmd_buf);
|
||||
this->end_active_command_encoder();
|
||||
[cmd_buf encodeWaitForEvent:event value:signal_value];
|
||||
register_encoder_counters();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -63,7 +63,7 @@ void node_tex_voronoi_f1_1d(vec3 coord,
|
|||
params.max_distance = 0.5 + 0.5 * params.randomness;
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, w);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outW = Output.Position.w;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ void node_tex_voronoi_smooth_f1_1d(vec3 coord,
|
|||
params.max_distance = 0.5 + 0.5 * params.randomness;
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, w);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outW = Output.Position.w;
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ void node_tex_voronoi_f2_1d(vec3 coord,
|
|||
params.max_distance = (0.5 + 0.5 * params.randomness) * 2.0;
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, w);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outW = Output.Position.w;
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ void node_tex_voronoi_f1_2d(vec3 coord,
|
|||
params.max_distance = voronoi_distance(vec2(0.0), vec2(0.5 + 0.5 * params.randomness), params);
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, coord.xy);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
}
|
||||
|
||||
|
@ -238,7 +238,7 @@ void node_tex_voronoi_smooth_f1_2d(vec3 coord,
|
|||
params.max_distance = voronoi_distance(vec2(0.0), vec2(0.5 + 0.5 * params.randomness), params);
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, coord.xy);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ void node_tex_voronoi_f2_2d(vec3 coord,
|
|||
2.0;
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, coord.xy);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ void node_tex_voronoi_f1_3d(vec3 coord,
|
|||
params.max_distance = voronoi_distance(vec3(0.0), vec3(0.5 + 0.5 * params.randomness), params);
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, coord);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
}
|
||||
|
||||
|
@ -384,7 +384,7 @@ void node_tex_voronoi_smooth_f1_3d(vec3 coord,
|
|||
params.max_distance = voronoi_distance(vec3(0.0), vec3(0.5 + 0.5 * params.randomness), params);
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, coord);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
}
|
||||
|
||||
|
@ -415,7 +415,7 @@ void node_tex_voronoi_f2_3d(vec3 coord,
|
|||
2.0;
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, coord);
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
}
|
||||
|
||||
|
@ -501,7 +501,7 @@ void node_tex_voronoi_f1_4d(vec3 coord,
|
|||
params.max_distance = voronoi_distance(vec4(0.0), vec4(0.5 + 0.5 * params.randomness), params);
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, vec4(coord, w));
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
outW = Output.Position.w;
|
||||
}
|
||||
|
@ -533,7 +533,7 @@ void node_tex_voronoi_smooth_f1_4d(vec3 coord,
|
|||
params.max_distance = voronoi_distance(vec4(0.0), vec4(0.5 + 0.5 * params.randomness), params);
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, vec4(coord, w));
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
outW = Output.Position.w;
|
||||
}
|
||||
|
@ -566,7 +566,7 @@ void node_tex_voronoi_f2_4d(vec3 coord,
|
|||
2.0;
|
||||
VoronoiOutput Output = fractal_voronoi_x_fx(params, vec4(coord, w));
|
||||
outDistance = Output.Distance;
|
||||
outColor.xyz = Output.Color;
|
||||
outColor = vec4(Output.Color, 1.0);
|
||||
outPosition = Output.Position.xyz;
|
||||
outW = Output.Position.w;
|
||||
}
|
||||
|
|
|
@ -522,11 +522,7 @@ void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float b
|
|||
ImBuf *IMB_loadifffile(int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr);
|
||||
|
||||
ImBuf *IMB_half_x(ImBuf *ibuf1);
|
||||
ImBuf *IMB_double_fast_x(ImBuf *ibuf1);
|
||||
ImBuf *IMB_double_x(ImBuf *ibuf1);
|
||||
ImBuf *IMB_half_y(ImBuf *ibuf1);
|
||||
ImBuf *IMB_double_fast_y(ImBuf *ibuf1);
|
||||
ImBuf *IMB_double_y(ImBuf *ibuf1);
|
||||
|
||||
void IMB_flipx(ImBuf *ibuf);
|
||||
void IMB_flipy(ImBuf *ibuf);
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
struct ImBuf;
|
||||
|
||||
void imb_filterx(ImBuf *ibuf);
|
||||
|
||||
void IMB_premultiply_rect(uint8_t *rect, char planes, int w, int h);
|
||||
void IMB_premultiply_rect_float(float *rect_float, int channels, int w, int h);
|
||||
|
||||
|
|
|
@ -19,44 +19,6 @@
|
|||
|
||||
#include "imbuf.hh"
|
||||
|
||||
static void filtrow(uchar *point, int x)
|
||||
{
|
||||
uint c1, c2, c3, error;
|
||||
|
||||
if (x > 1) {
|
||||
c1 = c2 = *point;
|
||||
error = 2;
|
||||
for (x--; x > 0; x--) {
|
||||
c3 = point[4];
|
||||
c1 += (c2 << 1) + c3 + error;
|
||||
error = c1 & 3;
|
||||
*point = c1 >> 2;
|
||||
point += 4;
|
||||
c1 = c2;
|
||||
c2 = c3;
|
||||
}
|
||||
*point = (c1 + (c2 << 1) + c2 + error) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void filtrowf(float *point, int x)
|
||||
{
|
||||
float c1, c2, c3;
|
||||
|
||||
if (x > 1) {
|
||||
c1 = c2 = *point;
|
||||
for (x--; x > 0; x--) {
|
||||
c3 = point[4];
|
||||
c1 += (c2 * 2) + c3;
|
||||
*point = 0.25f * c1;
|
||||
point += 4;
|
||||
c1 = c2;
|
||||
c2 = c3;
|
||||
}
|
||||
*point = 0.25f * (c1 + (c2 * 2) + c2);
|
||||
}
|
||||
}
|
||||
|
||||
static void filtcolum(uchar *point, int y, int skip)
|
||||
{
|
||||
uint c1, c2, c3, error;
|
||||
|
@ -137,43 +99,6 @@ void IMB_filtery(ImBuf *ibuf)
|
|||
}
|
||||
}
|
||||
|
||||
void imb_filterx(ImBuf *ibuf)
|
||||
{
|
||||
uchar *point = ibuf->byte_buffer.data;
|
||||
float *pointf = ibuf->float_buffer.data;
|
||||
|
||||
int x = ibuf->x;
|
||||
int y = ibuf->y;
|
||||
int skip = (x << 2) - 3;
|
||||
|
||||
for (; y > 0; y--) {
|
||||
if (point) {
|
||||
if (ibuf->planes > 24) {
|
||||
filtrow(point, x);
|
||||
}
|
||||
point++;
|
||||
filtrow(point, x);
|
||||
point++;
|
||||
filtrow(point, x);
|
||||
point++;
|
||||
filtrow(point, x);
|
||||
point += skip;
|
||||
}
|
||||
if (pointf) {
|
||||
if (ibuf->planes > 24) {
|
||||
filtrowf(pointf, x);
|
||||
}
|
||||
pointf++;
|
||||
filtrowf(pointf, x);
|
||||
pointf++;
|
||||
filtrowf(pointf, x);
|
||||
pointf++;
|
||||
filtrowf(pointf, x);
|
||||
pointf += skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void imb_filterN(ImBuf *out, ImBuf *in)
|
||||
{
|
||||
BLI_assert(out->channels == in->channels);
|
||||
|
@ -291,12 +216,6 @@ static void imb_filterN(ImBuf *out, ImBuf *in)
|
|||
}
|
||||
}
|
||||
|
||||
void IMB_filter(ImBuf *ibuf)
|
||||
{
|
||||
IMB_filtery(ibuf);
|
||||
imb_filterx(ibuf);
|
||||
}
|
||||
|
||||
void IMB_mask_filter_extend(char *mask, int width, int height)
|
||||
{
|
||||
const char *row1, *row2, *row3;
|
||||
|
|
|
@ -102,68 +102,6 @@ ImBuf *IMB_half_x(ImBuf *ibuf1)
|
|||
return ibuf2;
|
||||
}
|
||||
|
||||
ImBuf *IMB_double_fast_x(ImBuf *ibuf1)
|
||||
{
|
||||
ImBuf *ibuf2;
|
||||
int *p1, *dest, i, col, do_rect, do_float;
|
||||
float *p1f, *destf;
|
||||
|
||||
if (ibuf1 == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (ibuf1->byte_buffer.data == nullptr && ibuf1->float_buffer.data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
do_rect = (ibuf1->byte_buffer.data != nullptr);
|
||||
do_float = (ibuf1->float_buffer.data != nullptr);
|
||||
|
||||
ibuf2 = IMB_allocImBuf(2 * ibuf1->x, ibuf1->y, ibuf1->planes, ibuf1->flags);
|
||||
if (ibuf2 == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p1 = (int *)ibuf1->byte_buffer.data;
|
||||
dest = (int *)ibuf2->byte_buffer.data;
|
||||
p1f = (float *)ibuf1->float_buffer.data;
|
||||
destf = (float *)ibuf2->float_buffer.data;
|
||||
|
||||
for (i = ibuf1->y * ibuf1->x; i > 0; i--) {
|
||||
if (do_rect) {
|
||||
col = *p1++;
|
||||
*dest++ = col;
|
||||
*dest++ = col;
|
||||
}
|
||||
if (do_float) {
|
||||
destf[0] = destf[4] = p1f[0];
|
||||
destf[1] = destf[5] = p1f[1];
|
||||
destf[2] = destf[6] = p1f[2];
|
||||
destf[3] = destf[7] = p1f[3];
|
||||
destf += 8;
|
||||
p1f += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return ibuf2;
|
||||
}
|
||||
|
||||
ImBuf *IMB_double_x(ImBuf *ibuf1)
|
||||
{
|
||||
ImBuf *ibuf2;
|
||||
|
||||
if (ibuf1 == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (ibuf1->byte_buffer.data == nullptr && ibuf1->float_buffer.data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ibuf2 = IMB_double_fast_x(ibuf1);
|
||||
|
||||
imb_filterx(ibuf2);
|
||||
return ibuf2;
|
||||
}
|
||||
|
||||
static void imb_half_y_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
|
||||
{
|
||||
uchar *p1, *p2, *_p1, *dest;
|
||||
|
@ -256,70 +194,6 @@ ImBuf *IMB_half_y(ImBuf *ibuf1)
|
|||
return ibuf2;
|
||||
}
|
||||
|
||||
ImBuf *IMB_double_fast_y(ImBuf *ibuf1)
|
||||
{
|
||||
ImBuf *ibuf2;
|
||||
int *p1, *dest1, *dest2;
|
||||
float *p1f, *dest1f, *dest2f;
|
||||
int x, y;
|
||||
|
||||
if (ibuf1 == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (ibuf1->byte_buffer.data == nullptr && ibuf1->float_buffer.data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bool do_rect = (ibuf1->byte_buffer.data != nullptr);
|
||||
const bool do_float = (ibuf1->float_buffer.data != nullptr);
|
||||
|
||||
ibuf2 = IMB_allocImBuf(ibuf1->x, 2 * ibuf1->y, ibuf1->planes, ibuf1->flags);
|
||||
if (ibuf2 == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p1 = (int *)ibuf1->byte_buffer.data;
|
||||
dest1 = (int *)ibuf2->byte_buffer.data;
|
||||
p1f = (float *)ibuf1->float_buffer.data;
|
||||
dest1f = (float *)ibuf2->float_buffer.data;
|
||||
|
||||
for (y = ibuf1->y; y > 0; y--) {
|
||||
if (do_rect) {
|
||||
dest2 = dest1 + ibuf2->x;
|
||||
for (x = ibuf2->x; x > 0; x--) {
|
||||
*dest1++ = *dest2++ = *p1++;
|
||||
}
|
||||
dest1 = dest2;
|
||||
}
|
||||
if (do_float) {
|
||||
dest2f = dest1f + (4 * ibuf2->x);
|
||||
for (x = ibuf2->x * 4; x > 0; x--) {
|
||||
*dest1f++ = *dest2f++ = *p1f++;
|
||||
}
|
||||
dest1f = dest2f;
|
||||
}
|
||||
}
|
||||
|
||||
return ibuf2;
|
||||
}
|
||||
|
||||
ImBuf *IMB_double_y(ImBuf *ibuf1)
|
||||
{
|
||||
ImBuf *ibuf2;
|
||||
|
||||
if (ibuf1 == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (ibuf1->byte_buffer.data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ibuf2 = IMB_double_fast_y(ibuf1);
|
||||
|
||||
IMB_filtery(ibuf2);
|
||||
return ibuf2;
|
||||
}
|
||||
|
||||
/* pretty much specific functions which converts uchar <-> ushort but assumes
|
||||
* ushort range of 255*255 which is more convenient here
|
||||
*/
|
||||
|
|
|
@ -356,5 +356,5 @@ ImbAnimType imb_get_anim_type(const char *filepath)
|
|||
bool IMB_isanim(const char *filepath)
|
||||
{
|
||||
ImbAnimType type = imb_get_anim_type(filepath);
|
||||
return (type != ImbAnimType::NotAnim && type != ImbAnimType::Sequence);
|
||||
return !ELEM(type, ImbAnimType::NotAnim, ImbAnimType::Sequence);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "RNA_types.hh"
|
||||
|
|
|
@ -32,15 +32,6 @@ typedef struct AssetTag {
|
|||
char name[64]; /* MAX_NAME */
|
||||
} AssetTag;
|
||||
|
||||
#
|
||||
#
|
||||
typedef struct AssetFilterSettings {
|
||||
/** Tags to match against. These are newly allocated, and compared against the
|
||||
* #AssetMetaData.tags. */
|
||||
ListBase tags; /* AssetTag */
|
||||
uint64_t id_types; /* rna_enum_id_type_filter_items */
|
||||
} AssetFilterSettings;
|
||||
|
||||
/**
|
||||
* \brief The meta-data of an asset.
|
||||
* By creating and giving this for a data-block (#ID.asset_data), the data-block becomes an asset.
|
||||
|
|
|
@ -942,7 +942,7 @@
|
|||
.seed = 1, \
|
||||
.mat_rpl = 0, \
|
||||
}
|
||||
|
||||
|
||||
#define _DNA_DEFAULT_GreasePencilWeightProximityModifierData \
|
||||
{ \
|
||||
.target_vgname = "", \
|
||||
|
@ -950,5 +950,16 @@
|
|||
.dist_start = 0.0f, \
|
||||
.dist_end = 20.0f, \
|
||||
}
|
||||
|
||||
|
||||
#define _DNA_DEFAULT_GreasePencilHookModifierData \
|
||||
{ \
|
||||
.object = NULL, \
|
||||
.subtarget = "", \
|
||||
.flag = 0, \
|
||||
.falloff_type = MOD_GREASE_PENCIL_HOOK_Falloff_Smooth, \
|
||||
.parentinv = _DNA_DEFAULT_UNIT_M4, \
|
||||
.cent = {0.0f, 0.0f, 0.0f}, \
|
||||
.falloff = 0.0f, \
|
||||
.force = 0.5f, \
|
||||
}
|
||||
/* clang-format off */
|
||||
|
|
|
@ -111,6 +111,7 @@ typedef enum ModifierType {
|
|||
eModifierType_GreasePencilWeightAngle = 74,
|
||||
eModifierType_GreasePencilArray = 75,
|
||||
eModifierType_GreasePencilWeightProximity = 76,
|
||||
eModifierType_GreasePencilHook = 77,
|
||||
NUM_MODIFIER_TYPES,
|
||||
} ModifierType;
|
||||
|
||||
|
@ -2947,3 +2948,42 @@ typedef enum GreasePencilWeightProximityFlag {
|
|||
MOD_GREASE_PENCIL_WEIGHT_PROXIMITY_INVERT_OUTPUT = (1 << 0),
|
||||
MOD_GREASE_PENCIL_WEIGHT_PROXIMITY_MULTIPLY_DATA = (1 << 1),
|
||||
} GreasePencilWeightProximityFlag;
|
||||
|
||||
typedef struct GreasePencilHookModifierData {
|
||||
ModifierData modifier;
|
||||
GreasePencilModifierInfluenceData influence;
|
||||
|
||||
struct Object *object;
|
||||
/** Optional name of bone target, MAX_ID_NAME-2. */
|
||||
char subtarget[64];
|
||||
char _pad[4];
|
||||
|
||||
/** #GreasePencilHookFlag. */
|
||||
int flag;
|
||||
/** #GreasePencilHookFalloff. */
|
||||
char falloff_type;
|
||||
char _pad1[3];
|
||||
/** Matrix making current transform unmodified. */
|
||||
float parentinv[4][4];
|
||||
/** Visualization of hook. */
|
||||
float cent[3];
|
||||
/** If not zero, falloff is distance where influence zero. */
|
||||
float falloff;
|
||||
float force;
|
||||
} GreasePencilHookModifierData;
|
||||
|
||||
typedef enum GreasePencilHookFlag {
|
||||
MOD_GRAESE_PENCIL_HOOK_UNIFORM_SPACE = (1 << 0),
|
||||
} GreasePencilHookFlag;
|
||||
|
||||
typedef enum GreasePencilHookFalloff {
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_None = 0,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_Curve = 1,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_Sharp = 2,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_Smooth = 3,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_Root = 4,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_Linear = 5,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_Const = 6,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_Sphere = 7,
|
||||
MOD_GREASE_PENCIL_HOOK_Falloff_InvSquare = 8,
|
||||
} GreasePencilHookFalloff;
|
||||
|
|
|
@ -341,6 +341,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilMultiModifierData);
|
|||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilWeightAngleModifierData);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilArrayModifierData);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilWeightProximityModifierData);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilHookModifierData);
|
||||
|
||||
#undef SDNA_DEFAULT_DECL_STRUCT
|
||||
|
||||
|
@ -559,6 +560,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
|
|||
SDNA_DEFAULT_DECL(GreasePencilNoiseModifierData),
|
||||
SDNA_DEFAULT_DECL(GreasePencilLengthModifierData),
|
||||
SDNA_DEFAULT_DECL(GreasePencilWeightAngleModifierData),
|
||||
SDNA_DEFAULT_DECL(GreasePencilHookModifierData),
|
||||
|
||||
/* Grease Pencil 3.0 defaults. */
|
||||
SDNA_DEFAULT_DECL(GreasePencilSmoothModifierData),
|
||||
|
|
|
@ -328,6 +328,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
|
|||
ICON_VOLUME_DATA,
|
||||
"Volume Displace",
|
||||
"Deform volume based on noise or other vector fields"}, /* TODO: Use correct icon. */
|
||||
{eModifierType_GreasePencilHook,
|
||||
"GREASE_PENCIL_HOOK",
|
||||
ICON_HOOK,
|
||||
"Hook",
|
||||
"Deform stroke points using objects"},
|
||||
{eModifierType_GreasePencilNoise,
|
||||
"GREASE_PENCIL_NOISE",
|
||||
ICON_MOD_NOISE,
|
||||
|
@ -987,6 +992,7 @@ RNA_MOD_OBJECT_SET(GreasePencilMirror, object, OB_EMPTY);
|
|||
RNA_MOD_OBJECT_SET(GreasePencilTint, object, OB_EMPTY);
|
||||
RNA_MOD_OBJECT_SET(GreasePencilLattice, object, OB_LATTICE);
|
||||
RNA_MOD_OBJECT_SET(GreasePencilWeightProximity, object, OB_EMPTY);
|
||||
RNA_MOD_OBJECT_SET(GreasePencilHook, object, OB_EMPTY);
|
||||
|
||||
static void rna_HookModifier_object_set(PointerRNA *ptr,
|
||||
PointerRNA value,
|
||||
|
@ -1905,6 +1911,7 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilLength);
|
|||
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilWeightAngle);
|
||||
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilArray);
|
||||
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilWeightProximity);
|
||||
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilHook);
|
||||
|
||||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset);
|
||||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity);
|
||||
|
@ -1917,6 +1924,7 @@ RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLattice);
|
|||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLength);
|
||||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightAngle);
|
||||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightProximity);
|
||||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilHook);
|
||||
|
||||
static void rna_GreasePencilOpacityModifier_opacity_factor_range(
|
||||
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
|
||||
|
@ -9068,6 +9076,103 @@ static void rna_def_modifier_grease_pencil_multiply(BlenderRNA *brna)
|
|||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_grease_pencil_hook(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
static const EnumPropertyItem hook_falloff_items[] = {
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_None, "NONE", 0, "No Falloff", ""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_Curve, "CURVE", 0, "Curve", ""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_Smooth, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_Sphere, "SPHERE", ICON_SPHERECURVE, "Sphere", ""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_Root, "ROOT", ICON_ROOTCURVE, "Root", ""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_InvSquare,
|
||||
"INVERSE_SQUARE",
|
||||
ICON_ROOTCURVE,
|
||||
"Inverse Square",
|
||||
""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_Sharp, "SHARP", ICON_SHARPCURVE, "Sharp", ""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_Linear, "LINEAR", ICON_LINCURVE, "Linear", ""},
|
||||
{MOD_GREASE_PENCIL_HOOK_Falloff_Const, "CONSTANT", ICON_NOCURVE, "Constant", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
srna = RNA_def_struct(brna, "GreasePencilHookModifier", "Modifier");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "Hook Modifier", "Hook modifier to modify the location of stroke points");
|
||||
RNA_def_struct_sdna(srna, "GreasePencilHookModifierData");
|
||||
RNA_def_struct_ui_icon(srna, ICON_HOOK);
|
||||
|
||||
rna_def_modifier_grease_pencil_layer_filter(srna);
|
||||
rna_def_modifier_grease_pencil_material_filter(
|
||||
srna, "rna_GreasePencilHookModifier_material_filter_set");
|
||||
rna_def_modifier_grease_pencil_vertex_group(
|
||||
srna, "rna_GreasePencilHookModifier_vertex_group_name_set");
|
||||
rna_def_modifier_grease_pencil_custom_curve(srna);
|
||||
|
||||
rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0);
|
||||
rna_def_modifier_panel_open_prop(srna, "open_falloff_panel", 1);
|
||||
|
||||
RNA_define_lib_overridable(true);
|
||||
|
||||
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Object", "Parent Object for hook, also recalculates and clears offset");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, nullptr, "rna_GreasePencilHookModifier_object_set", nullptr, nullptr);
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
|
||||
|
||||
prop = RNA_def_property(srna, "subtarget", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, nullptr, "subtarget");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Sub-Target",
|
||||
"Name of Parent Bone for hook (if applicable), also recalculates and clears offset");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
|
||||
|
||||
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "force");
|
||||
RNA_def_property_range(prop, 0, 1);
|
||||
RNA_def_property_ui_text(prop, "Strength", "Relative force of the hook");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, hook_falloff_items); /* share the enum */
|
||||
RNA_def_property_ui_text(prop, "Falloff Type", "");
|
||||
RNA_def_property_translation_context(prop,
|
||||
BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "falloff");
|
||||
RNA_def_property_range(prop, 0, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0, 100, 100, 2);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Radius", "If not zero, the distance from the hook where influence ends");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "center", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "cent");
|
||||
RNA_def_property_ui_text(prop, "Hook Center", "");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "matrix_inverse", PROP_FLOAT, PROP_MATRIX);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "parentinv");
|
||||
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Matrix", "Reverse the transformation between this object and its target");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_falloff_uniform", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_HOOK_UNIFORM_SPACE);
|
||||
RNA_def_property_ui_text(prop, "Uniform Falloff", "Compensate for non-uniform object scale");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_grease_pencil_weight_proximity(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
@ -9324,6 +9429,7 @@ void RNA_def_modifier(BlenderRNA *brna)
|
|||
rna_def_modifier_grease_pencil_weight_angle(brna);
|
||||
rna_def_modifier_grease_pencil_array(brna);
|
||||
rna_def_modifier_grease_pencil_weight_proximity(brna);
|
||||
rna_def_modifier_grease_pencil_hook(brna);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,6 +37,7 @@ const EnumPropertyItem rna_enum_icon_items[] = {
|
|||
|
||||
# include "DNA_asset_types.h"
|
||||
|
||||
# include "ED_asset_filter.hh"
|
||||
# include "ED_geometry.hh"
|
||||
# include "ED_node.hh"
|
||||
# include "ED_object.hh"
|
||||
|
@ -720,7 +721,7 @@ static void rna_uiTemplateAssetView(uiLayout *layout,
|
|||
const char *drag_opname,
|
||||
PointerRNA *r_drag_op_properties)
|
||||
{
|
||||
AssetFilterSettings filter_settings{};
|
||||
blender::ed::asset::AssetFilterSettings filter_settings{};
|
||||
filter_settings.id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL;
|
||||
|
||||
uiTemplateAssetView(layout,
|
||||
|
|
|
@ -47,6 +47,7 @@ set(SRC
|
|||
intern/MOD_grease_pencil_array.cc
|
||||
intern/MOD_grease_pencil_color.cc
|
||||
intern/MOD_grease_pencil_dash.cc
|
||||
intern/MOD_grease_pencil_hook.cc
|
||||
intern/MOD_grease_pencil_lattice.cc
|
||||
intern/MOD_grease_pencil_length.cc
|
||||
intern/MOD_grease_pencil_mirror.cc
|
||||
|
|
|
@ -89,6 +89,7 @@ extern ModifierTypeInfo modifierType_GreasePencilLength;
|
|||
extern ModifierTypeInfo modifierType_GreasePencilWeightAngle;
|
||||
extern ModifierTypeInfo modifierType_GreasePencilArray;
|
||||
extern ModifierTypeInfo modifierType_GreasePencilWeightProximity;
|
||||
extern ModifierTypeInfo modifierType_GreasePencilHook;
|
||||
|
||||
/* MOD_util.cc */
|
||||
|
||||
|
|
|
@ -0,0 +1,361 @@
|
|||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup modifiers
|
||||
*/
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_math_rotation.hh"
|
||||
#include "BLI_string.h" /* For #STRNCPY. */
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_lib_query.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "MOD_grease_pencil_util.hh"
|
||||
#include "MOD_modifiertypes.hh"
|
||||
#include "MOD_ui_common.hh"
|
||||
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void init_data(ModifierData *md)
|
||||
{
|
||||
auto *gpmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
|
||||
|
||||
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
|
||||
|
||||
MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(GreasePencilHookModifierData), modifier);
|
||||
modifier::greasepencil::init_influence_data(&gpmd->influence, true);
|
||||
}
|
||||
|
||||
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
|
||||
{
|
||||
const auto *gmd = reinterpret_cast<const GreasePencilHookModifierData *>(md);
|
||||
auto *tgmd = reinterpret_cast<GreasePencilHookModifierData *>(target);
|
||||
|
||||
BKE_modifier_copydata_generic(md, target, flag);
|
||||
modifier::greasepencil::copy_influence_data(&gmd->influence, &tgmd->influence, flag);
|
||||
}
|
||||
|
||||
static void free_data(ModifierData *md)
|
||||
{
|
||||
auto *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
|
||||
|
||||
modifier::greasepencil::free_influence_data(&mmd->influence);
|
||||
}
|
||||
|
||||
static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
|
||||
{
|
||||
auto *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
|
||||
|
||||
return (mmd->object == nullptr);
|
||||
}
|
||||
|
||||
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
|
||||
{
|
||||
auto *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
|
||||
if (mmd->object != nullptr) {
|
||||
DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier");
|
||||
}
|
||||
DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier");
|
||||
}
|
||||
|
||||
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
|
||||
{
|
||||
auto *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
|
||||
|
||||
modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
|
||||
}
|
||||
|
||||
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
|
||||
{
|
||||
const auto *mmd = reinterpret_cast<const GreasePencilHookModifierData *>(md);
|
||||
|
||||
BLO_write_struct(writer, GreasePencilHookModifierData, mmd);
|
||||
modifier::greasepencil::write_influence_data(writer, &mmd->influence);
|
||||
}
|
||||
|
||||
static void blend_read(BlendDataReader *reader, ModifierData *md)
|
||||
{
|
||||
auto *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
|
||||
modifier::greasepencil::read_influence_data(reader, &mmd->influence);
|
||||
}
|
||||
|
||||
/* Calculate the factor of falloff. */
|
||||
static float hook_falloff(const float falloff,
|
||||
const int falloff_type,
|
||||
const float falloff_sq,
|
||||
const float fac_orig,
|
||||
const CurveMapping *curfalloff,
|
||||
const float len_sq)
|
||||
{
|
||||
BLI_assert(falloff_sq);
|
||||
if (len_sq > falloff_sq) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (len_sq <= 0.0f) {
|
||||
return fac_orig;
|
||||
}
|
||||
if (falloff_type == MOD_GREASE_PENCIL_HOOK_Falloff_Const) {
|
||||
return fac_orig;
|
||||
}
|
||||
else if (falloff_type == MOD_GREASE_PENCIL_HOOK_Falloff_InvSquare) {
|
||||
/* Avoid sqrt below. */
|
||||
return (1.0f - (len_sq / falloff_sq)) * fac_orig;
|
||||
}
|
||||
|
||||
float fac = 1.0f - (math::sqrt(len_sq) / falloff);
|
||||
|
||||
switch (falloff_type) {
|
||||
case MOD_GREASE_PENCIL_HOOK_Falloff_Curve:
|
||||
return BKE_curvemapping_evaluateF(curfalloff, 0, fac) * fac_orig;
|
||||
case MOD_GREASE_PENCIL_HOOK_Falloff_Sharp:
|
||||
return fac * fac * fac_orig;
|
||||
case MOD_GREASE_PENCIL_HOOK_Falloff_Smooth:
|
||||
return (3.0f * fac * fac - 2.0f * fac * fac * fac) * fac_orig;
|
||||
case MOD_GREASE_PENCIL_HOOK_Falloff_Root:
|
||||
return math::sqrt(fac) * fac_orig;
|
||||
break;
|
||||
case MOD_GREASE_PENCIL_HOOK_Falloff_Sphere:
|
||||
return math::sqrt(2 * fac - fac * fac) * fac_orig;
|
||||
case MOD_GREASE_PENCIL_HOOK_Falloff_Linear:
|
||||
ATTR_FALLTHROUGH; /* Pass. */
|
||||
default:
|
||||
return fac * fac_orig;
|
||||
}
|
||||
}
|
||||
|
||||
static void deform_drawing(const ModifierData &md,
|
||||
const Object &ob,
|
||||
bke::greasepencil::Drawing &drawing)
|
||||
{
|
||||
const auto &mmd = reinterpret_cast<const GreasePencilHookModifierData &>(md);
|
||||
bke::CurvesGeometry &curves = drawing.strokes_for_write();
|
||||
if (curves.points_num() == 0) {
|
||||
return;
|
||||
}
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask strokes = modifier::greasepencil::get_filtered_stroke_mask(
|
||||
&ob, curves, mmd.influence, memory);
|
||||
if (strokes.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const VArray<float> input_weights = modifier::greasepencil::get_influence_vertex_weights(
|
||||
curves, mmd.influence);
|
||||
|
||||
const int falloff_type = mmd.falloff_type;
|
||||
const float falloff = (mmd.falloff_type == eHook_Falloff_None) ? 0.0f : mmd.falloff;
|
||||
const float falloff_sq = square_f(falloff);
|
||||
const float fac_orig = mmd.force;
|
||||
const bool use_falloff = falloff_sq != 0.0f;
|
||||
const bool use_uniform = (mmd.flag & MOD_GRAESE_PENCIL_HOOK_UNIFORM_SPACE) != 0;
|
||||
|
||||
const float3x3 mat_uniform = use_uniform ? float3x3(float4x4(mmd.parentinv)) :
|
||||
float3x3::identity();
|
||||
const float3 cent = use_uniform ? math::transform_point(mat_uniform, float3(mmd.cent)) :
|
||||
float3(mmd.cent);
|
||||
|
||||
float4x4 dmat;
|
||||
/* Get world-space matrix of target, corrected for the space the verts are in. */
|
||||
if (mmd.subtarget[0]) {
|
||||
bPoseChannel *pchan = BKE_pose_channel_find_name(mmd.object->pose, mmd.subtarget);
|
||||
if (pchan) {
|
||||
/* Bone target if there's a matching pose-channel. */
|
||||
dmat = mmd.object->object_to_world() * float4x4(pchan->pose_mat);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Just object target. */
|
||||
dmat = mmd.object->object_to_world();
|
||||
}
|
||||
float4x4 use_mat = ob.world_to_object() * dmat * float4x4(mmd.parentinv);
|
||||
|
||||
auto get_weight = [&](const int point) {
|
||||
const float weight = input_weights[point];
|
||||
if (mmd.influence.flag & GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP) {
|
||||
return 1.0f - weight;
|
||||
}
|
||||
return weight;
|
||||
};
|
||||
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
|
||||
strokes.foreach_index(blender::GrainSize(128), [&](const int stroke) {
|
||||
const IndexRange points_range = points_by_curve[stroke].index_range();
|
||||
for (const int point_i : points_range) {
|
||||
const int point = point_i + points_by_curve[stroke].first();
|
||||
const float weight = get_weight(point);
|
||||
if (weight < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float fac;
|
||||
if (use_falloff) {
|
||||
float len_sq;
|
||||
if (use_uniform) {
|
||||
const float3 co_uniform = math::transform_point(mat_uniform, positions[point]);
|
||||
len_sq = math::distance(cent, co_uniform);
|
||||
}
|
||||
else {
|
||||
len_sq = math::distance(cent, positions[point]);
|
||||
}
|
||||
fac = hook_falloff(
|
||||
falloff, falloff_type, falloff_sq, fac_orig, mmd.influence.custom_curve, len_sq);
|
||||
}
|
||||
else {
|
||||
fac = fac_orig;
|
||||
}
|
||||
|
||||
if (fac != 0.0f) {
|
||||
const float3 co_tmp = math::transform_point(use_mat, positions[point]);
|
||||
positions[point] = math::interpolate(positions[point], co_tmp, fac * weight);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
drawing.tag_positions_changed();
|
||||
}
|
||||
|
||||
static void modify_geometry_set(ModifierData *md,
|
||||
const ModifierEvalContext *ctx,
|
||||
bke::GeometrySet *geometry_set)
|
||||
{
|
||||
const GreasePencilHookModifierData *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
|
||||
|
||||
if (!geometry_set->has_grease_pencil()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
|
||||
|
||||
const int current_frame = grease_pencil.runtime->eval_frame;
|
||||
|
||||
IndexMaskMemory mask_memory;
|
||||
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
|
||||
grease_pencil, mmd->influence, mask_memory);
|
||||
const Vector<bke::greasepencil::Drawing *> drawings =
|
||||
modifier::greasepencil::get_drawings_for_write(grease_pencil, layer_mask, current_frame);
|
||||
|
||||
threading::parallel_for_each(drawings, [&](bke::greasepencil::Drawing *drawing) {
|
||||
deform_drawing(*md, *ctx->object, *drawing);
|
||||
});
|
||||
}
|
||||
|
||||
static void panel_draw(const bContext *C, Panel *panel)
|
||||
{
|
||||
uiLayout *layout = panel->layout;
|
||||
|
||||
PointerRNA ob_ptr;
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
|
||||
|
||||
PointerRNA hook_object_ptr = RNA_pointer_get(ptr, "object");
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiItemR(col, ptr, "object", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
if (!RNA_pointer_is_null(&hook_object_ptr) &&
|
||||
RNA_enum_get(&hook_object_ptr, "type") == OB_ARMATURE)
|
||||
{
|
||||
PointerRNA hook_object_data_ptr = RNA_pointer_get(&hook_object_ptr, "data");
|
||||
uiItemPointerR(
|
||||
col, ptr, "subtarget", &hook_object_data_ptr, "bones", IFACE_("Bone"), ICON_NONE);
|
||||
}
|
||||
|
||||
uiItemR(layout, ptr, "strength", UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
|
||||
|
||||
if (uiLayout *sub = uiLayoutPanelProp(C, layout, ptr, "open_falloff_panel", "Falloff")) {
|
||||
uiLayoutSetPropSep(sub, true);
|
||||
|
||||
uiItemR(sub, ptr, "falloff_type", UI_ITEM_NONE, IFACE_("Type"), ICON_NONE);
|
||||
|
||||
bool use_falloff = RNA_enum_get(ptr, "falloff_type") != eWarp_Falloff_None;
|
||||
|
||||
uiLayout *row = uiLayoutRow(sub, false);
|
||||
uiLayoutSetActive(row, use_falloff);
|
||||
uiItemR(row, ptr, "falloff_radius", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
uiItemR(sub, ptr, "use_falloff_uniform", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
if (RNA_enum_get(ptr, "falloff_type") == eWarp_Falloff_Curve) {
|
||||
uiTemplateCurveMapping(sub, ptr, "custom_curve", 0, false, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (uiLayout *influence_panel = uiLayoutPanelProp(
|
||||
C, layout, ptr, "open_influence_panel", "Influence"))
|
||||
{
|
||||
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
|
||||
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
|
||||
modifier::greasepencil::draw_vertex_group_settings(C, influence_panel, ptr);
|
||||
}
|
||||
|
||||
modifier_panel_end(layout, ptr);
|
||||
}
|
||||
|
||||
static void panel_register(ARegionType *region_type)
|
||||
{
|
||||
modifier_panel_register(region_type, eModifierType_GreasePencilHook, panel_draw);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
ModifierTypeInfo modifierType_GreasePencilHook = {
|
||||
/*idname*/ "GreasePencilHookModifier",
|
||||
/*name*/ N_("Hook"),
|
||||
/*struct_name*/ "GreasePencilHookModifierData",
|
||||
/*struct_size*/ sizeof(GreasePencilHookModifierData),
|
||||
/*srna*/ &RNA_GreasePencilHookModifier,
|
||||
/*type*/ ModifierTypeType::OnlyDeform,
|
||||
/*flags*/
|
||||
eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
|
||||
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping,
|
||||
/*icon*/ ICON_HOOK,
|
||||
|
||||
/*copy_data*/ blender::copy_data,
|
||||
|
||||
/*deform_verts*/ nullptr,
|
||||
/*deform_matrices*/ nullptr,
|
||||
/*deform_verts_EM*/ nullptr,
|
||||
/*deform_matrices_EM*/ nullptr,
|
||||
/*modify_mesh*/ nullptr,
|
||||
/*modify_geometry_set*/ blender::modify_geometry_set,
|
||||
|
||||
/*init_data*/ blender::init_data,
|
||||
/*required_data_mask*/ nullptr,
|
||||
/*free_data*/ blender::free_data,
|
||||
/*is_disabled*/ blender::is_disabled,
|
||||
/*update_depsgraph*/ blender::update_depsgraph,
|
||||
/*depends_on_time*/ nullptr,
|
||||
/*depends_on_normals*/ nullptr,
|
||||
/*foreach_ID_link*/ blender::foreach_ID_link,
|
||||
/*foreach_tex_link*/ nullptr,
|
||||
/*free_runtime_data*/ nullptr,
|
||||
/*panel_register*/ blender::panel_register,
|
||||
/*blend_write*/ blender::blend_write,
|
||||
/*blend_read*/ blender::blend_read,
|
||||
};
|
|
@ -117,8 +117,6 @@ static void deform_drawing(const GreasePencilNoiseModifierData &mmd,
|
|||
return;
|
||||
}
|
||||
|
||||
const OffsetIndices<int> points_by_curve = strokes.points_by_curve();
|
||||
|
||||
int seed = mmd.seed;
|
||||
/* Make sure different modifiers get different seeds. */
|
||||
seed += BLI_hash_string(ob.id.name + 2);
|
||||
|
@ -133,12 +131,17 @@ static void deform_drawing(const GreasePencilNoiseModifierData &mmd,
|
|||
}
|
||||
}
|
||||
|
||||
const OffsetIndices<int> points_by_curve = strokes.points_by_curve();
|
||||
const VArray<float> vgroup_weights = modifier::greasepencil::get_influence_vertex_weights(
|
||||
strokes, mmd.influence);
|
||||
|
||||
auto get_weight = [&](const IndexRange points, const int point_i) {
|
||||
const float vertex_weight = vgroup_weights[points[point_i]];
|
||||
if (!use_curve) {
|
||||
return 1.0f;
|
||||
return vertex_weight;
|
||||
}
|
||||
const float value = float(point_i) / float(points.size() - 1);
|
||||
return BKE_curvemapping_evaluateF(mmd.influence.custom_curve, 0, value);
|
||||
return vertex_weight * BKE_curvemapping_evaluateF(mmd.influence.custom_curve, 0, value);
|
||||
};
|
||||
|
||||
auto get_noise = [](const Array<float> &noise_table, const float value) {
|
||||
|
|
|
@ -2293,16 +2293,9 @@ static void panel_draw(const bContext *C, Panel *panel)
|
|||
uiLayoutSetPropDecorate(layout, false);
|
||||
|
||||
if (!(nmd->flag & NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR)) {
|
||||
uiTemplateID(layout,
|
||||
C,
|
||||
ptr,
|
||||
"node_group",
|
||||
"node.new_geometry_node_group_assign",
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
false,
|
||||
nullptr);
|
||||
const char *newop = (nmd->node_group == nullptr) ? "node.new_geometry_node_group_assign" :
|
||||
"object.geometry_node_tree_copy_assign";
|
||||
uiTemplateID(layout, C, ptr, "node_group", newop, nullptr, nullptr, 0, false, nullptr);
|
||||
}
|
||||
|
||||
if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) {
|
||||
|
|
|
@ -280,5 +280,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
|
|||
INIT_TYPE(GreasePencilWeightAngle);
|
||||
INIT_TYPE(GreasePencilArray);
|
||||
INIT_TYPE(GreasePencilWeightProximity);
|
||||
INIT_TYPE(GreasePencilHook);
|
||||
#undef INIT_TYPE
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ static void align_rotations_auto_pivot(const IndexMask &mask,
|
|||
{
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 vector = vectors[i];
|
||||
if (is_zero_v3(vector)) {
|
||||
if (math::is_zero(vector)) {
|
||||
output_rotations[i] = input_rotations[i];
|
||||
return;
|
||||
}
|
||||
|
@ -53,10 +53,10 @@ static void align_rotations_auto_pivot(const IndexMask &mask,
|
|||
|
||||
const float3 new_axis = math::normalize(vector);
|
||||
float3 rotation_axis = math::cross_high_precision(old_axis, new_axis);
|
||||
if (is_zero_v3(rotation_axis)) {
|
||||
if (math::is_zero(rotation_axis)) {
|
||||
/* The vectors are linearly dependent, so we fall back to another axis. */
|
||||
rotation_axis = math::cross_high_precision(old_axis, float3(1, 0, 0));
|
||||
if (is_zero_v3(rotation_axis)) {
|
||||
if (math::is_zero(rotation_axis)) {
|
||||
/* This is now guaranteed to not be zero. */
|
||||
rotation_axis = math::cross_high_precision(old_axis, float3(0, 1, 0));
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ static void align_rotations_fixed_pivot(const IndexMask &mask,
|
|||
}
|
||||
|
||||
const float3 vector = vectors[i];
|
||||
if (is_zero_v3(vector)) {
|
||||
if (math::is_zero(vector)) {
|
||||
output_rotations[i] = input_rotations[i];
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ struct SplitGroups {
|
|||
VectorSet<int> group_ids;
|
||||
|
||||
IndexMaskMemory memory;
|
||||
Array<IndexMask> group_masks;
|
||||
Vector<IndexMask> group_masks;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -90,24 +90,8 @@ struct SplitGroups {
|
|||
return true;
|
||||
}
|
||||
|
||||
const VArray<int> group_ids_varray = field_evaluator.get_evaluated<int>(0);
|
||||
if (selection.size() == domain_size && group_ids_varray.is_single()) {
|
||||
const int group_id = group_ids_varray.get_internal_single();
|
||||
ensure_group_geometries(geometry_by_group_id, {group_id});
|
||||
geometry_by_group_id.lookup(group_id)->add(src_component);
|
||||
return true;
|
||||
}
|
||||
|
||||
const VArraySpan<int> group_ids = group_ids_varray;
|
||||
selection.foreach_index_optimized<int>(
|
||||
[&](const int i) { r_groups.group_ids.add(group_ids[i]); });
|
||||
|
||||
r_groups.group_masks.reinitialize(r_groups.group_ids.size());
|
||||
IndexMask::from_groups<int>(
|
||||
selection,
|
||||
r_groups.memory,
|
||||
[&](const int i) { return r_groups.group_ids.index_of(group_ids[i]); },
|
||||
r_groups.group_masks);
|
||||
r_groups.group_masks = IndexMask::from_group_ids(
|
||||
selection, field_evaluator.get_evaluated<int>(0), r_groups.memory, r_groups.group_ids);
|
||||
|
||||
ensure_group_geometries(geometry_by_group_id, r_groups.group_ids);
|
||||
return false;
|
||||
|
|
|
@ -96,6 +96,7 @@ static PyStructSequence_Field app_cb_info_fields[] = {
|
|||
{"_extension_repos_update_post", "on changes to extension repos (after)"},
|
||||
{"_extension_repos_sync", "on creating or synchronizing the active repository"},
|
||||
{"_extension_repos_upgrade", "on upgrading the active repository"},
|
||||
{"_extension_drop_url", "on dropping a URL"},
|
||||
|
||||
/* sets the permanent tag */
|
||||
#define APP_CB_OTHER_FIELDS 1
|
||||
|
|
|
@ -2092,9 +2092,7 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
|
|||
rd,
|
||||
preview ? scene->r.psfra : scene->r.sfra,
|
||||
scene->r.cfra,
|
||||
(int *)ibuf->byte_buffer.data,
|
||||
ibuf->x,
|
||||
ibuf->y,
|
||||
ibuf,
|
||||
suffix,
|
||||
reports))
|
||||
{
|
||||
|
@ -2126,9 +2124,7 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
|
|||
rd,
|
||||
preview ? scene->r.psfra : scene->r.sfra,
|
||||
scene->r.cfra,
|
||||
(int *)ibuf_arr[2]->byte_buffer.data,
|
||||
ibuf_arr[2]->x,
|
||||
ibuf_arr[2]->y,
|
||||
ibuf_arr[2],
|
||||
"",
|
||||
reports))
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ static void __cpuid(
|
|||
}
|
||||
#endif
|
||||
|
||||
static int cpu_supports_sse42(void)
|
||||
static int cpu_supports_sse42()
|
||||
{
|
||||
int result[4], num;
|
||||
__cpuid(result, 0);
|
||||
|
@ -39,17 +39,17 @@ static int cpu_supports_sse42(void)
|
|||
|
||||
if (num >= 1) {
|
||||
__cpuid(result, 0x00000001);
|
||||
return (result[2] & ((int)1 << 20)) != 0;
|
||||
return (result[2] & (int(1) << 20)) != 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *cpu_brand_string(void)
|
||||
static const char *cpu_brand_string()
|
||||
{
|
||||
static char buf[49] = {0};
|
||||
int result[4] = {0};
|
||||
__cpuid(result, 0x80000000);
|
||||
if (result[0] >= (int)0x80000004) {
|
||||
if (result[0] >= int(0x80000004)) {
|
||||
__cpuid((int *)(buf + 0), 0x80000002);
|
||||
__cpuid((int *)(buf + 16), 0x80000003);
|
||||
__cpuid((int *)(buf + 32), 0x80000004);
|
||||
|
@ -60,7 +60,7 @@ static const char *cpu_brand_string(void)
|
|||
}
|
||||
return buf_ptr;
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -91,7 +91,7 @@ BOOL WINAPI DllMain(HINSTANCE /* hinstDLL */, DWORD fdwReason, LPVOID /* lpvRese
|
|||
# include <cstdio>
|
||||
# include <cstdlib>
|
||||
|
||||
static __attribute__((constructor)) void cpu_check(void)
|
||||
static __attribute__((constructor)) void cpu_check()
|
||||
{
|
||||
# ifdef __x86_64
|
||||
if (!cpu_supports_sse42()) {
|
||||
|
|
|
@ -429,7 +429,7 @@ class Report:
|
|||
"-o", diff_color_img,
|
||||
)
|
||||
try:
|
||||
subprocess.check_output(command)
|
||||
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if self.verbose:
|
||||
print_message(e.output.decode("utf-8", 'ignore'))
|
||||
|
@ -447,7 +447,7 @@ class Report:
|
|||
"-o", diff_alpha_img,
|
||||
)
|
||||
try:
|
||||
subprocess.check_output(command)
|
||||
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if self.verbose:
|
||||
msg = e.output.decode("utf-8", 'ignore')
|
||||
|
|
|
@ -327,7 +327,7 @@ def process_commands(cmake_dir: str, data: Sequence[str]) -> Optional[ProcessedC
|
|||
return None
|
||||
|
||||
# Check for unsupported configurations.
|
||||
for arg in ("WITH_UNITY_BUILD", "WITH_COMPILER_CCACHE"):
|
||||
for arg in ("WITH_UNITY_BUILD", "WITH_COMPILER_CCACHE", "WITH_COMPILER_PRECOMPILED_HEADERS"):
|
||||
if cmake_cache_var_is_true(cmake_cache_var(cmake_dir, arg)):
|
||||
sys.stderr.write("The option '%s' must be disabled for proper functionality\n" % arg)
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue