Refactor: Store BLF Glyphs in blender::Map #118528

Merged
Harley Acheson merged 9 commits from Harley/blender:GlyphMap into main 2024-02-21 18:17:30 +01:00
94 changed files with 1721 additions and 666 deletions
Showing only changes of commit 8ab0931873 - Show all commits

View File

@ -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)

View File

@ -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=""
)

View File

@ -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);
}
}

View File

@ -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) { \

View File

@ -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",

View File

@ -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,

View File

@ -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')

View File

@ -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'

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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;
}
/** \} */

View File

@ -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.
*/

View File

@ -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

View File

@ -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,
};

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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. */

View File

@ -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;
}

View File

@ -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);

View File

@ -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) {

View File

@ -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++) {

View File

@ -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;

View File

@ -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

View File

@ -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 &parameters)
: 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(&parameters.num_total),
num_local(&parameters.num_local),
num_linked(&parameters.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 &parame
* this call.
*/
UnusedIdsData data{
bmain,
0,
true,
parameters.do_linked_ids,
parameters.do_recursive,
parameters.do_local_ids ? &parameters.num_total : &num_dummy,
&parameters.num_local,
(parameters.do_local_ids && parameters.do_linked_ids) ? &parameters.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,
&parameters.num_total,
&parameters.num_local,
&parameters.num_linked};
UnusedIDsData data(bmain, tag, parameters);
if (parameters.do_recursive) {
BKE_main_relations_create(bmain, 0);

View File

@ -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)

View File

@ -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);
}
}
}

View File

@ -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()

View File

@ -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);

View File

@ -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;

View File

@ -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()

View File

@ -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);
}

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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. */

View File

@ -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);

View File

@ -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);
}

View File

@ -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:

View File

@ -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.
*

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -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();
}

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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
/** \} */

View File

@ -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) {

View File

@ -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);
}

View File

@ -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);

View File

@ -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 = "";

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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();
}
/** \} */

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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
*/

View File

@ -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);
}

View File

@ -4,6 +4,8 @@
#pragma once
#include <memory>
#include "DEG_depsgraph.hh"
#include "RNA_types.hh"

View File

@ -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.

View File

@ -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 */

View File

@ -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;

View File

@ -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),

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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 */

View File

@ -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,
};

View File

@ -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) {

View File

@ -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) {

View File

@ -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
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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))
{

View File

@ -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()) {

View File

@ -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')

View File

@ -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