Anim: Per bone wire width for custom shapes #120176

Merged
Christoph Lendenfeld merged 48 commits from ChrisLend/blender:bone_wire_width into main 2024-05-30 15:51:42 +02:00
160 changed files with 2182 additions and 1249 deletions
Showing only changes of commit 68dc0f83ea - Show all commits

View File

@ -870,19 +870,29 @@ else()
endif()
# Vulkan
if(NOT APPLE)
option(WITH_VULKAN_BACKEND "Enable Vulkan as graphics backend (experimental)" ON)
option(WITH_VULKAN_GUARDEDALLOC "\
Use guardedalloc for host allocations done inside Vulkan (development option)"
OFF
)
option(WITH_VULKAN_BACKEND "Enable Vulkan as graphics backend (experimental)" ON)
option(WITH_VULKAN_GUARDEDALLOC "Use guardedalloc for host allocations done inside Vulkan (development option)"
OFF
)
mark_as_advanced(
WITH_VULKAN_BACKEND
WITH_VULKAN_GUARDEDALLOC
)
if(APPLE)
option(WITH_VULKAN_MOLTENVK "Enable Vulkan over MoltenVK (development option)" OFF)
mark_as_advanced(
WITH_VULKAN_BACKEND
WITH_VULKAN_GUARDEDALLOC
WITH_VULKAN_MOLTENVK
)
if(NOT WITH_EXPERIMENTAL_FEATURES)
set(WITH_VULKAN_BACKEND OFF)
endif()
if(APPLE AND NOT WITH_VULKAN_MOLTENVK)
set(WITH_VULKAN_BACKEND OFF)
endif()
if(NOT WITH_EXPERIMENTAL_FEATURES)
if (APPLE)
set(WITH_VULKAN_MOLTENVK OFF)
endif()
set(WITH_VULKAN_BACKEND OFF)
endif()
# Metal

View File

@ -43,11 +43,11 @@ find_path(MOLTENVK_INCLUDE_DIR
find_library(MOLTENVK_LIBRARY
NAMES
MoltenVK
libMoltenVK.dylib
HINTS
${_moltenvk_SEARCH_DIRS}
PATH_SUFFIXES
dylib/macOS
dynamic/dylib/macOS
)
# handle the QUIETLY and REQUIRED arguments and set MOLTENVK_FOUND to TRUE if

View File

@ -108,6 +108,12 @@ if(WITH_OPENSUBDIV)
endif()
add_bundled_libraries(opensubdiv/lib)
if(WITH_VULKAN_BACKEND)
find_package(MoltenVK REQUIRED)
find_package(ShaderC REQUIRED)
find_package(Vulkan REQUIRED)
endif()
if(WITH_CODEC_SNDFILE)
find_package(SndFile)
find_library(_sndfile_FLAC_LIBRARY NAMES flac HINTS ${LIBDIR}/sndfile/lib)

View File

@ -30,7 +30,7 @@ but some areas are still being extended and improved.
Before Starting
===============
This document its intended to familiarize you with Blender Python API
This document is intended to familiarize you with Blender Python API
but not to fully cover each topic.
A quick list of helpful things to know before starting:

View File

@ -77,8 +77,12 @@ void device_metal_info(vector<DeviceInfo> &devices)
if (@available(macos 14.0, *)) {
info.use_hardware_raytracing = device.supportsRaytracing;
/* Use hardware raytracing for faster rendering on architectures that support it. */
info.use_metalrt_by_default = (MetalInfo::get_apple_gpu_architecture(device) >= APPLE_M3);
info.use_metalrt_by_default = false;
if (vendor == METAL_GPU_APPLE) {
/* Use hardware raytracing for faster rendering on architectures that support it. */
info.use_metalrt_by_default = (MetalInfo::get_apple_gpu_architecture(device) >=
APPLE_M3);
}
}
}
# endif

View File

@ -945,7 +945,9 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_wo);
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
# ifdef __LIGHT_TREE__
INTEGRATOR_STATE_WRITE(state, ray, previous_dt) = ray->tmax - ray->tmin;
if (kernel_data.integrator.use_light_tree) {
INTEGRATOR_STATE_WRITE(state, ray, previous_dt) = ray->tmax - ray->tmin;
}
# endif
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
# ifdef __RAY_DIFFERENTIALS__

View File

@ -75,9 +75,7 @@ KERNEL_STRUCT_MEMBER(ray, float, tmax, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING)
#ifdef __LIGHT_TREE__
KERNEL_STRUCT_MEMBER(ray, float, previous_dt, KERNEL_FEATURE_PATH_TRACING)
#endif
KERNEL_STRUCT_MEMBER(ray, float, previous_dt, KERNEL_FEATURE_LIGHT_TREE)
KERNEL_STRUCT_END(ray)
/*************************** Intersection result ******************************/

View File

@ -1748,6 +1748,9 @@ enum KernelFeatureFlag : uint32_t {
/* Use denoising kernels and output denoising passes. */
KERNEL_FEATURE_DENOISING = (1U << 29U),
/* Light tree. */
KERNEL_FEATURE_LIGHT_TREE = (1U << 30U),
};
/* Shader node feature mask, to specialize shader evaluation for kernels. */

View File

@ -345,6 +345,10 @@ uint Integrator::get_kernel_features() const
kernel_features |= KERNEL_FEATURE_AO_ADDITIVE;
}
if (get_use_light_tree()) {
kernel_features |= KERNEL_FEATURE_LIGHT_TREE;
}
return kernel_features;
}

View File

@ -32,10 +32,6 @@
#include <sys/stat.h>
/* Set to 0 to allow devices that do not have the required features.
* This allows development on OSX until we really needs these features. */
#define STRICT_REQUIREMENTS true
/*
* Should we only select surfaces that are known to be compatible. Or should we in case no
* compatible surfaces have been found select the first one.
@ -226,14 +222,15 @@ class GHOST_DeviceVK {
queue_create_infos.push_back(graphic_queue_create_info);
VkPhysicalDeviceFeatures device_features = {};
#if STRICT_REQUIREMENTS
#ifndef __APPLE__
device_features.geometryShader = VK_TRUE;
device_features.dualSrcBlend = VK_TRUE;
/* MoltenVK supports logicOp, needs to be build with MVK_USE_METAL_PRIVATE_API. */
device_features.logicOp = VK_TRUE;
#endif
device_features.dualSrcBlend = VK_TRUE;
device_features.imageCubeArray = VK_TRUE;
device_features.multiViewport = VK_TRUE;
device_features.shaderClipDistance = VK_TRUE;
#endif
device_features.drawIndirectFirstInstance = VK_TRUE;
device_features.fragmentStoresAndAtomics = VK_TRUE;
device_features.samplerAnisotropy = features.features.samplerAnisotropy;
@ -355,7 +352,11 @@ static GHOST_TSuccess ensure_vulkan_device(VkInstance vk_instance,
}
}
#if STRICT_REQUIREMENTS
#ifdef __APPLE__
if (!device_vk.features.features.dualSrcBlend || !device_vk.features.features.imageCubeArray) {
continue;
}
#else
if (!device_vk.features.features.geometryShader || !device_vk.features.features.dualSrcBlend ||
!device_vk.features.features.logicOp || !device_vk.features.features.imageCubeArray)
{
@ -1000,8 +1001,7 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
if (use_window_surface) {
const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension();
requireExtension(extensions_available, extensions_enabled, "VK_KHR_surface");
requireExtension(extensions_available, extensions_enabled, VK_KHR_SURFACE_EXTENSION_NAME);
requireExtension(extensions_available, extensions_enabled, native_surface_extension_name);
extensions_device.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
@ -1010,10 +1010,13 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
extensions_device.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
/* Enable MoltenVK required instance extensions. */
#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME
#ifdef __APPLE__
requireExtension(
extensions_available, extensions_enabled, "VK_KHR_get_physical_device_properties2");
extensions_available, extensions_enabled, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
#endif
requireExtension(extensions_available,
extensions_enabled,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
VkInstance instance = VK_NULL_HANDLE;
if (!vulkan_device.has_value()) {
@ -1046,6 +1049,10 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
create_info.pNext = &validationFeatures;
}
#ifdef __APPLE__
create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif
VK_CHECK(vkCreateInstance(&create_info, nullptr, &instance));
}
else {

View File

@ -56,12 +56,11 @@ size_t malloc_usable_size(void *ptr);
#endif
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
// Needed for memalign on Linux and _aligned_alloc on Windows.
/* Needed for `memalign` on Linux and _aligned_alloc on Windows. */
# include <malloc.h>
#else
// Apple's malloc is 16-byte aligned, and does not have malloc.h, so include
// stdilb instead.
/* Apple's malloc is 16-byte aligned, and does not have malloc.h, so include stdilb instead. */
# include <stdlib.h>
#endif

View File

@ -76,7 +76,7 @@ the model.
* \ingroup intern_sky_modal
*/
// Uses Sep 9 pattern / Aug 23 mean dataset
/* Uses Sep 9 pattern / Aug 23 mean dataset. */
static const double datasetXYZ1[] = {
// albedo 0, turbidity 1

View File

@ -773,7 +773,7 @@ const bTheme U_theme_default = {
.sub_back = RGBA(0x0000001f),
},
.shade2 = RGBA(0x4d4d4de6),
.hilite = RGBA(0x65a2ffff),
.hilite = RGBA(0x71a8ffff),
.grid = RGBA(0x1d1d1dff),
.vertex_size = 3,
.outline_width = 1,
@ -941,7 +941,7 @@ const bTheme U_theme_default = {
.console_input = RGBA(0xf2f2f2ff),
.console_info = RGBA(0x95d600ff),
.console_error = RGBA(0xff4d84ff),
.console_cursor = RGBA(0xff0000ff),
.console_cursor = RGBA(0x71a8ffff),
.console_select = RGBA(0xffffff30),
.vertex_size = 3,
.outline_width = 1,

View File

@ -78,7 +78,7 @@ Comment[wa]=Modelaedje 3D, animåcion, rindou eyet après-produccion
Comment[zh_HK]=3D
Comment[zh_CN]=3D
Comment[zh_TW]=3D
Keywords=3d;cg;modeling;animation;painting;sculpting;texturing;video editing;video tracking;rendering;render engine;cycles;game engine;python;
Keywords=3d;cg;modeling;animation;painting;sculpting;texturing;video editing;video tracking;rendering;render engine;cycles;python;
Exec=blender %f
Icon=blender
Terminal=false

View File

@ -406,7 +406,12 @@ def draw_keymaps(context, layout):
rowsubsub = rowsub.row(align=True)
if not ok:
rowsubsub.alert = True
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM')
search_placeholder = ""
if spref.filter_type == 'NAME':
search_placeholder = iface_("Search by Name")
elif spref.filter_type == 'KEY':
search_placeholder = iface_("Search by Key-Binding")
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM', placeholder=search_placeholder)
if not filter_text:
# When the keyconfig defines its own preferences.

View File

@ -369,7 +369,6 @@
speaker="#000000"
vertex="#000000"
vertex_select="#ff7a00"
vertex_active="#000000"
vertex_size="3"
vertex_bevel="#00a5ff"
vertex_unreferenced="#000000"
@ -735,7 +734,6 @@
grid="#353535ff"
vertex="#000000"
vertex_select="#ff8500"
vertex_active="#000000"
vertex_size="3"
vertex_bevel="#000000"
vertex_unreferenced="#000000"
@ -1168,7 +1166,7 @@
line_input="#f2f2f2"
line_info="#95d600"
line_error="#ff4d84"
cursor="#ff0000"
cursor="#71a8ff"
select="#ffffff30"
>
<space>

View File

@ -3656,7 +3656,7 @@ def km_animation_channels(params):
# ------------------------------------------------------------------------------
# Object Grease Pencil Modes
def km_grease_pencil(params):
def km_gpencil_legacy(params):
items = []
keymap = (
"Grease Pencil",
@ -3694,7 +3694,7 @@ def km_grease_pencil(params):
return keymap
def _grease_pencil_selection(params, *, alt_select=False):
def _gpencil_legacy_selection(params, *, alt_select=False):
return [
# Select all
*_template_items_select_actions(params, "gpencil.select_all"),
@ -3727,7 +3727,7 @@ def _grease_pencil_selection(params, *, alt_select=False):
]
def _grease_pencil_display():
def _gpencil_legacy_display():
return [
("wm.context_toggle", {"type": 'Q', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'space_data.overlay.use_gpencil_edit_lines')]}),
@ -3736,7 +3736,7 @@ def _grease_pencil_display():
]
def km_grease_pencil_stroke_edit_mode(params):
def km_gpencil_legacy_stroke_edit_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Edit Mode",
@ -3751,7 +3751,7 @@ def km_grease_pencil_stroke_edit_mode(params):
(op_tool_cycle, "builtin.interpolate"), params),
("gpencil.interpolate_sequence", {"type": 'E', "value": 'PRESS', "shift": True, "ctrl": True}, None),
# Selection
*_grease_pencil_selection(params),
*_gpencil_legacy_selection(params),
("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'ADD')]}),
("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True},
@ -3795,7 +3795,7 @@ def km_grease_pencil_stroke_edit_mode(params):
*_template_items_hide_reveal_actions("gpencil.hide", "gpencil.reveal"),
("gpencil.selection_opacity_toggle", {"type": 'H', "value": 'PRESS', "ctrl": True}, None),
# Display
*_grease_pencil_display(),
*_gpencil_legacy_display(),
# Isolate layer
("gpencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
# Move to layer
@ -3843,7 +3843,7 @@ def km_grease_pencil_stroke_edit_mode(params):
return keymap
def km_grease_pencil_stroke_curve_edit_mode(_params):
def km_gpencil_legacy_stroke_curve_edit_mode(_params):
items = []
keymap = (
"Grease Pencil Stroke Curve Edit Mode",
@ -3859,7 +3859,7 @@ def km_grease_pencil_stroke_curve_edit_mode(_params):
return keymap
def km_grease_pencil_stroke_paint_mode(params):
def km_gpencil_legacy_stroke_paint_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Paint Mode",
@ -3911,7 +3911,7 @@ def km_grease_pencil_stroke_paint_mode(params):
return keymap
def km_grease_pencil_stroke_paint_draw_brush(params):
def km_gpencil_legacy_stroke_paint_draw_brush(params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Draw brush)",
@ -3944,7 +3944,7 @@ def km_grease_pencil_stroke_paint_draw_brush(params):
return keymap
def km_grease_pencil_stroke_paint_erase(_params):
def km_gpencil_legacy_stroke_paint_erase(_params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Erase)",
@ -3963,7 +3963,7 @@ def km_grease_pencil_stroke_paint_erase(_params):
return keymap
def km_grease_pencil_stroke_paint_fill(_params):
def km_gpencil_legacy_stroke_paint_fill(_params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Fill)",
@ -3999,7 +3999,7 @@ def km_grease_pencil_stroke_paint_fill(_params):
return keymap
def km_grease_pencil_stroke_paint_tint(_params):
def km_gpencil_legacy_stroke_paint_tint(_params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Tint)",
@ -4018,7 +4018,7 @@ def km_grease_pencil_stroke_paint_tint(_params):
return keymap
def km_grease_pencil_stroke_sculpt_mode(params):
def km_gpencil_legacy_stroke_sculpt_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt Mode",
@ -4028,7 +4028,7 @@ def km_grease_pencil_stroke_sculpt_mode(params):
items.extend([
# Selection
*_grease_pencil_selection(params, alt_select=True),
*_gpencil_legacy_selection(params, alt_select=True),
*_template_items_select_lasso(params, "gpencil.select_lasso"),
# Selection mode
("wm.context_toggle", {"type": 'ONE', "value": 'PRESS'},
@ -4051,7 +4051,7 @@ def km_grease_pencil_stroke_sculpt_mode(params):
# Copy
("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
# Display
*_grease_pencil_display(),
*_gpencil_legacy_display(),
# Active layer
op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}),
# Active material
@ -4077,7 +4077,7 @@ def km_grease_pencil_stroke_sculpt_mode(params):
return keymap
def km_grease_pencil_stroke_sculpt_smooth(_params):
def km_gpencil_legacy_stroke_sculpt_smooth(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Smooth)",
@ -4097,7 +4097,7 @@ def km_grease_pencil_stroke_sculpt_smooth(_params):
return keymap
def km_grease_pencil_stroke_sculpt_thickness(_params):
def km_gpencil_legacy_stroke_sculpt_thickness(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Thickness)",
@ -4117,7 +4117,7 @@ def km_grease_pencil_stroke_sculpt_thickness(_params):
return keymap
def km_grease_pencil_stroke_sculpt_strength(_params):
def km_gpencil_legacy_stroke_sculpt_strength(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Strength)",
@ -4137,7 +4137,7 @@ def km_grease_pencil_stroke_sculpt_strength(_params):
return keymap
def km_grease_pencil_stroke_sculpt_grab(_params):
def km_gpencil_legacy_stroke_sculpt_grab(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Grab)",
@ -4157,7 +4157,7 @@ def km_grease_pencil_stroke_sculpt_grab(_params):
return keymap
def km_grease_pencil_stroke_sculpt_push(_params):
def km_gpencil_legacy_stroke_sculpt_push(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Push)",
@ -4177,7 +4177,7 @@ def km_grease_pencil_stroke_sculpt_push(_params):
return keymap
def km_grease_pencil_stroke_sculpt_twist(_params):
def km_gpencil_legacy_stroke_sculpt_twist(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Twist)",
@ -4197,7 +4197,7 @@ def km_grease_pencil_stroke_sculpt_twist(_params):
return keymap
def km_grease_pencil_stroke_sculpt_pinch(_params):
def km_gpencil_legacy_stroke_sculpt_pinch(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Pinch)",
@ -4217,7 +4217,7 @@ def km_grease_pencil_stroke_sculpt_pinch(_params):
return keymap
def km_grease_pencil_stroke_sculpt_randomize(_params):
def km_gpencil_legacy_stroke_sculpt_randomize(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Randomize)",
@ -4237,7 +4237,7 @@ def km_grease_pencil_stroke_sculpt_randomize(_params):
return keymap
def km_grease_pencil_stroke_sculpt_clone(_params):
def km_gpencil_legacy_stroke_sculpt_clone(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Clone)",
@ -4257,7 +4257,7 @@ def km_grease_pencil_stroke_sculpt_clone(_params):
return keymap
def km_grease_pencil_stroke_weight_mode(params):
def km_gpencil_legacy_stroke_weight_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Weight Mode",
@ -4281,7 +4281,7 @@ def km_grease_pencil_stroke_weight_mode(params):
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 1.0 / 0.9)]}),
# Display
*_grease_pencil_display(),
*_gpencil_legacy_display(),
# Active layer
op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}),
# Merge Layer
@ -4310,7 +4310,7 @@ def km_grease_pencil_stroke_weight_mode(params):
return keymap
def km_grease_pencil_stroke_weight_draw(_params):
def km_gpencil_legacy_stroke_weight_draw(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Draw)",
@ -4327,7 +4327,7 @@ def km_grease_pencil_stroke_weight_draw(_params):
return keymap
def km_grease_pencil_stroke_weight_blur(_params):
def km_gpencil_legacy_stroke_weight_blur(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Blur)",
@ -4344,7 +4344,7 @@ def km_grease_pencil_stroke_weight_blur(_params):
return keymap
def km_grease_pencil_stroke_weight_average(_params):
def km_gpencil_legacy_stroke_weight_average(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Average)",
@ -4361,7 +4361,7 @@ def km_grease_pencil_stroke_weight_average(_params):
return keymap
def km_grease_pencil_stroke_weight_smear(_params):
def km_gpencil_legacy_stroke_weight_smear(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Smear)",
@ -4378,7 +4378,7 @@ def km_grease_pencil_stroke_weight_smear(_params):
return keymap
def km_grease_pencil_stroke_vertex_mode(params):
def km_gpencil_legacy_stroke_vertex_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Vertex Mode",
@ -4388,7 +4388,7 @@ def km_grease_pencil_stroke_vertex_mode(params):
items.extend([
# Selection
*_grease_pencil_selection(params, alt_select=True),
*_gpencil_legacy_selection(params, alt_select=True),
*_template_items_select_lasso(params, "gpencil.select_lasso"),
# Selection mode
("wm.context_toggle", {"type": 'ONE', "value": 'PRESS'},
@ -4413,7 +4413,7 @@ def km_grease_pencil_stroke_vertex_mode(params):
# Color Flip
("gpencil.tint_flip", {"type": 'X', "value": 'PRESS'}, None),
# Display
*_grease_pencil_display(),
*_gpencil_legacy_display(),
# Active layer
op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}),
# Merge Layer
@ -4433,7 +4433,7 @@ def km_grease_pencil_stroke_vertex_mode(params):
return keymap
def km_grease_pencil_stroke_vertex_draw(_params):
def km_gpencil_legacy_stroke_vertex_draw(_params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Draw)",
@ -4460,7 +4460,7 @@ def km_grease_pencil_stroke_vertex_draw(_params):
return keymap
def km_grease_pencil_stroke_vertex_blur(_params):
def km_gpencil_legacy_stroke_vertex_blur(_params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Blur)",
@ -4485,7 +4485,7 @@ def km_grease_pencil_stroke_vertex_blur(_params):
return keymap
def km_grease_pencil_stroke_vertex_average(_params):
def km_gpencil_legacy_stroke_vertex_average(_params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Average)",
@ -4512,7 +4512,7 @@ def km_grease_pencil_stroke_vertex_average(_params):
return keymap
def km_grease_pencil_stroke_vertex_smear(_params):
def km_gpencil_legacy_stroke_vertex_smear(_params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Smear)",
@ -4537,7 +4537,7 @@ def km_grease_pencil_stroke_vertex_smear(_params):
return keymap
def km_grease_pencil_stroke_vertex_replace(_params):
def km_gpencil_legacy_stroke_vertex_replace(_params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Replace)",
@ -4680,6 +4680,25 @@ def km_grease_pencil_edit_mode(params):
return keymap
def km_grease_pencil_sculpt_mode(params):
items = []
keymap = (
"Grease Pencil Sculpt Mode",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": items}
)
items.extend([
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 0.9)]}),
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 1.0 / 0.9)]}),
*_template_paint_radial_control("gpencil_sculpt_paint"),
])
return keymap
# ------------------------------------------------------------------------------
# Object/Pose Modes
@ -6143,6 +6162,7 @@ def km_edit_curves(params):
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},
{"properties": [("mode", 'CURVE_SHRINKFATTEN')]}),
("curves.cyclic_toggle", {"type": 'C', "value": 'PRESS', "alt": True}, None),
("curves.handle_type_set", {"type": 'V', "value": 'PRESS'}, None),
])
return keymap
@ -8598,38 +8618,40 @@ def generate_keymaps(params=None):
km_animation_channels(params),
# Modes.
km_grease_pencil(params), # TODO: Rename to km_annotate
km_grease_pencil_stroke_curve_edit_mode(params),
km_grease_pencil_stroke_edit_mode(params),
km_grease_pencil_stroke_paint_mode(params),
km_grease_pencil_stroke_paint_draw_brush(params),
km_grease_pencil_stroke_paint_erase(params),
km_grease_pencil_stroke_paint_fill(params),
km_grease_pencil_stroke_paint_tint(params),
km_grease_pencil_stroke_sculpt_mode(params),
km_grease_pencil_stroke_sculpt_smooth(params),
km_grease_pencil_stroke_sculpt_thickness(params),
km_grease_pencil_stroke_sculpt_strength(params),
km_grease_pencil_stroke_sculpt_grab(params),
km_grease_pencil_stroke_sculpt_push(params),
km_grease_pencil_stroke_sculpt_twist(params),
km_grease_pencil_stroke_sculpt_pinch(params),
km_grease_pencil_stroke_sculpt_randomize(params),
km_grease_pencil_stroke_sculpt_clone(params),
km_grease_pencil_stroke_weight_mode(params),
km_grease_pencil_stroke_weight_draw(params),
km_grease_pencil_stroke_weight_blur(params),
km_grease_pencil_stroke_weight_average(params),
km_grease_pencil_stroke_weight_smear(params),
km_grease_pencil_stroke_vertex_mode(params),
km_grease_pencil_stroke_vertex_draw(params),
km_grease_pencil_stroke_vertex_blur(params),
km_grease_pencil_stroke_vertex_average(params),
km_grease_pencil_stroke_vertex_smear(params),
km_grease_pencil_stroke_vertex_replace(params),
# Grease Pencil v2
km_gpencil_legacy(params), # TODO: Rename to km_annotate
km_gpencil_legacy_stroke_curve_edit_mode(params),
km_gpencil_legacy_stroke_edit_mode(params),
km_gpencil_legacy_stroke_paint_mode(params),
km_gpencil_legacy_stroke_paint_draw_brush(params),
km_gpencil_legacy_stroke_paint_erase(params),
km_gpencil_legacy_stroke_paint_fill(params),
km_gpencil_legacy_stroke_paint_tint(params),
km_gpencil_legacy_stroke_sculpt_mode(params),
km_gpencil_legacy_stroke_sculpt_smooth(params),
km_gpencil_legacy_stroke_sculpt_thickness(params),
km_gpencil_legacy_stroke_sculpt_strength(params),
km_gpencil_legacy_stroke_sculpt_grab(params),
km_gpencil_legacy_stroke_sculpt_push(params),
km_gpencil_legacy_stroke_sculpt_twist(params),
km_gpencil_legacy_stroke_sculpt_pinch(params),
km_gpencil_legacy_stroke_sculpt_randomize(params),
km_gpencil_legacy_stroke_sculpt_clone(params),
km_gpencil_legacy_stroke_weight_mode(params),
km_gpencil_legacy_stroke_weight_draw(params),
km_gpencil_legacy_stroke_weight_blur(params),
km_gpencil_legacy_stroke_weight_average(params),
km_gpencil_legacy_stroke_weight_smear(params),
km_gpencil_legacy_stroke_vertex_mode(params),
km_gpencil_legacy_stroke_vertex_draw(params),
km_gpencil_legacy_stroke_vertex_blur(params),
km_gpencil_legacy_stroke_vertex_average(params),
km_gpencil_legacy_stroke_vertex_smear(params),
km_gpencil_legacy_stroke_vertex_replace(params),
# Grease Pencil v3
km_grease_pencil_paint_mode(params),
km_grease_pencil_edit_mode(params),
km_grease_pencil_sculpt_mode(params),
# Object mode.
km_object_mode(params),
km_object_non_modal(params),

View File

@ -2341,7 +2341,7 @@ def km_animation_channels(params):
# Modes
def km_grease_pencil(_params):
def km_gpencil_legacy(_params):
items = []
keymap = (
"Grease Pencil",
@ -2352,7 +2352,7 @@ def km_grease_pencil(_params):
return keymap
def _grease_pencil_selection(params):
def _gpencil_legacy_selection(params):
return [
# Select all
("gpencil.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
@ -2369,7 +2369,7 @@ def _grease_pencil_selection(params):
]
def _grease_pencil_display():
def _gpencil_legacy_display():
return [
("wm.context_toggle", {"type": 'Q', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'space_data.overlay.use_gpencil_edit_lines')]}),
@ -2378,7 +2378,7 @@ def _grease_pencil_display():
]
def km_grease_pencil_stroke_edit_mode(params):
def km_gpencil_legacy_stroke_edit_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Edit Mode",
@ -2393,7 +2393,7 @@ def km_grease_pencil_stroke_edit_mode(params):
("gpencil.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True), ("toggle", True)]}),
# Selection
*_grease_pencil_selection(params),
*_gpencil_legacy_selection(params),
("gpencil.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
# Duplicate and move selected points
("gpencil.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
@ -2453,7 +2453,7 @@ def km_grease_pencil_stroke_edit_mode(params):
return keymap
def km_grease_pencil_stroke_paint_mode(params):
def km_gpencil_legacy_stroke_paint_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Paint Mode",
@ -2509,7 +2509,7 @@ def km_grease_pencil_stroke_paint_mode(params):
return keymap
def km_grease_pencil_stroke_paint_draw_brush(params):
def km_gpencil_legacy_stroke_paint_draw_brush(params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Draw brush)",
@ -2540,7 +2540,7 @@ def km_grease_pencil_stroke_paint_draw_brush(params):
return keymap
def km_grease_pencil_stroke_paint_erase(params):
def km_gpencil_legacy_stroke_paint_erase(params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Erase)",
@ -2559,7 +2559,7 @@ def km_grease_pencil_stroke_paint_erase(params):
return keymap
def km_grease_pencil_stroke_paint_fill(params):
def km_gpencil_legacy_stroke_paint_fill(params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Fill)",
@ -2584,7 +2584,7 @@ def km_grease_pencil_stroke_paint_fill(params):
return keymap
def km_grease_pencil_stroke_paint_tint(params):
def km_gpencil_legacy_stroke_paint_tint(params):
items = []
keymap = (
"Grease Pencil Stroke Paint (Tint)",
@ -2603,7 +2603,7 @@ def km_grease_pencil_stroke_paint_tint(params):
return keymap
def km_grease_pencil_stroke_sculpt_mode(params):
def km_gpencil_legacy_stroke_sculpt_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt Mode",
@ -2625,7 +2625,7 @@ def km_grease_pencil_stroke_sculpt_mode(params):
("gpencil.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "alt": True, "ctrl": True},
{"properties": [("use_shift_extend", True), ("toggle", True)]}),
# Selection
*_grease_pencil_selection(params),
*_gpencil_legacy_selection(params),
# Brush properties
("wm.radial_control", {"type": 'U', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt_paint.brush.strength')]}),
@ -2638,7 +2638,7 @@ def km_grease_pencil_stroke_sculpt_mode(params):
# Copy
("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
# Display
*_grease_pencil_display(),
*_gpencil_legacy_display(),
# Delete
op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'DEL', "value": 'PRESS'}),
@ -2663,7 +2663,7 @@ def km_grease_pencil_stroke_sculpt_mode(params):
return keymap
def km_grease_pencil_stroke_sculpt_smooth(_params):
def km_gpencil_legacy_stroke_sculpt_smooth(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Smooth)",
@ -2683,7 +2683,7 @@ def km_grease_pencil_stroke_sculpt_smooth(_params):
return keymap
def km_grease_pencil_stroke_sculpt_thickness(_params):
def km_gpencil_legacy_stroke_sculpt_thickness(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Thickness)",
@ -2703,7 +2703,7 @@ def km_grease_pencil_stroke_sculpt_thickness(_params):
return keymap
def km_grease_pencil_stroke_sculpt_strength(_params):
def km_gpencil_legacy_stroke_sculpt_strength(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Strength)",
@ -2723,7 +2723,7 @@ def km_grease_pencil_stroke_sculpt_strength(_params):
return keymap
def km_grease_pencil_stroke_sculpt_grab(_params):
def km_gpencil_legacy_stroke_sculpt_grab(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Grab)",
@ -2743,7 +2743,7 @@ def km_grease_pencil_stroke_sculpt_grab(_params):
return keymap
def km_grease_pencil_stroke_sculpt_push(_params):
def km_gpencil_legacy_stroke_sculpt_push(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Push)",
@ -2763,7 +2763,7 @@ def km_grease_pencil_stroke_sculpt_push(_params):
return keymap
def km_grease_pencil_stroke_sculpt_twist(_params):
def km_gpencil_legacy_stroke_sculpt_twist(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Twist)",
@ -2783,7 +2783,7 @@ def km_grease_pencil_stroke_sculpt_twist(_params):
return keymap
def km_grease_pencil_stroke_sculpt_pinch(_params):
def km_gpencil_legacy_stroke_sculpt_pinch(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Pinch)",
@ -2803,7 +2803,7 @@ def km_grease_pencil_stroke_sculpt_pinch(_params):
return keymap
def km_grease_pencil_stroke_sculpt_randomize(_params):
def km_gpencil_legacy_stroke_sculpt_randomize(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Randomize)",
@ -2823,7 +2823,7 @@ def km_grease_pencil_stroke_sculpt_randomize(_params):
return keymap
def km_grease_pencil_stroke_sculpt_clone(_params):
def km_gpencil_legacy_stroke_sculpt_clone(_params):
items = []
keymap = (
"Grease Pencil Stroke Sculpt (Clone)",
@ -2843,7 +2843,7 @@ def km_grease_pencil_stroke_sculpt_clone(_params):
return keymap
def km_grease_pencil_stroke_weight_mode(params):
def km_gpencil_legacy_stroke_weight_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Weight Mode",
@ -2888,7 +2888,7 @@ def km_grease_pencil_stroke_weight_mode(params):
return keymap
def km_grease_pencil_stroke_weight_draw(_params):
def km_gpencil_legacy_stroke_weight_draw(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Draw)",
@ -2906,7 +2906,7 @@ def km_grease_pencil_stroke_weight_draw(_params):
return keymap
def km_grease_pencil_stroke_weight_blur(_params):
def km_gpencil_legacy_stroke_weight_blur(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Blur)",
@ -2922,7 +2922,7 @@ def km_grease_pencil_stroke_weight_blur(_params):
return keymap
def km_grease_pencil_stroke_weight_average(_params):
def km_gpencil_legacy_stroke_weight_average(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Average)",
@ -2938,7 +2938,7 @@ def km_grease_pencil_stroke_weight_average(_params):
return keymap
def km_grease_pencil_stroke_weight_smear(_params):
def km_gpencil_legacy_stroke_weight_smear(_params):
items = []
keymap = (
"Grease Pencil Stroke Weight (Smear)",
@ -2954,7 +2954,7 @@ def km_grease_pencil_stroke_weight_smear(_params):
return keymap
def km_grease_pencil_stroke_vertex_mode(params):
def km_gpencil_legacy_stroke_vertex_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Vertex Mode",
@ -2976,7 +2976,7 @@ def km_grease_pencil_stroke_vertex_mode(params):
("gpencil.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "alt": True, "ctrl": True},
{"properties": [("use_shift_extend", True), ("toggle", True)]}),
# Selection
*_grease_pencil_selection(params),
*_gpencil_legacy_selection(params),
# Brush strength
("wm.radial_control", {"type": 'U', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')]}),
@ -2990,7 +2990,7 @@ def km_grease_pencil_stroke_vertex_mode(params):
# Color operators
("gpencil.tint_flip", {"type": 'X', "value": 'PRESS'}, None),
# Display
*_grease_pencil_display(),
*_gpencil_legacy_display(),
# Delete
op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'DEL', "value": 'PRESS'}),
@ -3013,7 +3013,7 @@ def km_grease_pencil_stroke_vertex_mode(params):
return keymap
def km_grease_pencil_stroke_vertex_draw(params):
def km_gpencil_legacy_stroke_vertex_draw(params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Draw)",
@ -3038,7 +3038,7 @@ def km_grease_pencil_stroke_vertex_draw(params):
return keymap
def km_grease_pencil_stroke_vertex_blur(params):
def km_gpencil_legacy_stroke_vertex_blur(params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Blur)",
@ -3061,7 +3061,7 @@ def km_grease_pencil_stroke_vertex_blur(params):
return keymap
def km_grease_pencil_stroke_vertex_average(params):
def km_gpencil_legacy_stroke_vertex_average(params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Average)",
@ -3086,7 +3086,7 @@ def km_grease_pencil_stroke_vertex_average(params):
return keymap
def km_grease_pencil_stroke_vertex_smear(params):
def km_gpencil_legacy_stroke_vertex_smear(params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Smear)",
@ -3109,7 +3109,7 @@ def km_grease_pencil_stroke_vertex_smear(params):
return keymap
def km_grease_pencil_stroke_vertex_replace(params):
def km_gpencil_legacy_stroke_vertex_replace(params):
items = []
keymap = (
"Grease Pencil Stroke Vertex (Replace)",
@ -4520,34 +4520,34 @@ def generate_keymaps_impl(params=None):
km_animation_channels(params),
# Modes.
# km_grease_pencil(params), # Empty.
km_grease_pencil_stroke_edit_mode(params),
km_grease_pencil_stroke_paint_mode(params),
km_grease_pencil_stroke_paint_draw_brush(params),
km_grease_pencil_stroke_paint_erase(params),
km_grease_pencil_stroke_paint_fill(params),
km_grease_pencil_stroke_paint_tint(params),
km_grease_pencil_stroke_sculpt_mode(params),
km_grease_pencil_stroke_sculpt_smooth(params),
km_grease_pencil_stroke_sculpt_thickness(params),
km_grease_pencil_stroke_sculpt_strength(params),
km_grease_pencil_stroke_sculpt_grab(params),
km_grease_pencil_stroke_sculpt_push(params),
km_grease_pencil_stroke_sculpt_twist(params),
km_grease_pencil_stroke_sculpt_pinch(params),
km_grease_pencil_stroke_sculpt_randomize(params),
km_grease_pencil_stroke_sculpt_clone(params),
km_grease_pencil_stroke_weight_mode(params),
km_grease_pencil_stroke_weight_draw(params),
km_grease_pencil_stroke_weight_blur(params),
km_grease_pencil_stroke_weight_average(params),
km_grease_pencil_stroke_weight_smear(params),
km_grease_pencil_stroke_vertex_mode(params),
km_grease_pencil_stroke_vertex_draw(params),
km_grease_pencil_stroke_vertex_blur(params),
km_grease_pencil_stroke_vertex_average(params),
km_grease_pencil_stroke_vertex_smear(params),
km_grease_pencil_stroke_vertex_replace(params),
# km_gpencil_legacy(params), # Empty.
km_gpencil_legacy_stroke_edit_mode(params),
km_gpencil_legacy_stroke_paint_mode(params),
km_gpencil_legacy_stroke_paint_draw_brush(params),
km_gpencil_legacy_stroke_paint_erase(params),
km_gpencil_legacy_stroke_paint_fill(params),
km_gpencil_legacy_stroke_paint_tint(params),
km_gpencil_legacy_stroke_sculpt_mode(params),
km_gpencil_legacy_stroke_sculpt_smooth(params),
km_gpencil_legacy_stroke_sculpt_thickness(params),
km_gpencil_legacy_stroke_sculpt_strength(params),
km_gpencil_legacy_stroke_sculpt_grab(params),
km_gpencil_legacy_stroke_sculpt_push(params),
km_gpencil_legacy_stroke_sculpt_twist(params),
km_gpencil_legacy_stroke_sculpt_pinch(params),
km_gpencil_legacy_stroke_sculpt_randomize(params),
km_gpencil_legacy_stroke_sculpt_clone(params),
km_gpencil_legacy_stroke_weight_mode(params),
km_gpencil_legacy_stroke_weight_draw(params),
km_gpencil_legacy_stroke_weight_blur(params),
km_gpencil_legacy_stroke_weight_average(params),
km_gpencil_legacy_stroke_weight_smear(params),
km_gpencil_legacy_stroke_vertex_mode(params),
km_gpencil_legacy_stroke_vertex_draw(params),
km_gpencil_legacy_stroke_vertex_blur(params),
km_gpencil_legacy_stroke_vertex_average(params),
km_gpencil_legacy_stroke_vertex_smear(params),
km_gpencil_legacy_stroke_vertex_replace(params),
km_face_mask(params),
km_weight_paint_vertex_selection(params),
km_pose(params),

View File

@ -71,7 +71,6 @@ class ANIM_OT_keying_set_export(Operator):
f.write("ks.use_insertkey_needed = %s\n" % ks.use_insertkey_needed)
f.write("ks.use_insertkey_visual = %s\n" % ks.use_insertkey_visual)
f.write("ks.use_insertkey_xyz_to_rgb = %s\n" % ks.use_insertkey_xyz_to_rgb)
f.write("\n")
# --------------------------------------------------------

View File

@ -589,6 +589,11 @@ class RemovePresetInterfaceTheme(AddPresetBase, Operator):
def invoke(self, context, event):
return context.window_manager.invoke_confirm(self, event, title="Remove Custom Theme", confirm_text="Delete")
def post_cb(self, context):
# Without this, the name & colors are kept after removing the theme.
# Even though the theme is removed from the list, it's seems like a bug to keep it displayed after removal.
bpy.ops.preferences.reset_default_theme()
class SavePresetInterfaceTheme(AddPresetBase, Operator):
"""Save a custom theme in the preset list"""

View File

@ -597,9 +597,15 @@ class PREFERENCES_OT_addon_install(Operator):
)
def _target_path_items(_self, context):
default_item = ('DEFAULT', "Default", "")
if context is None:
return (
default_item,
)
paths = context.preferences.filepaths
return (
('DEFAULT', "Default", ""),
default_item,
None,
*[(item.name, item.name, "") for index, item in enumerate(paths.script_directories) if item.directory],
)

View File

@ -3228,7 +3228,7 @@ class WM_MT_splash_quick_setup(Menu):
can_import = bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version
if can_import:
layout.label(text="Import Existing Settings")
layout.label(text="Import Preferences From Previous Version")
split = layout.split(factor=0.20) # Left margin.
split.label()
@ -3236,17 +3236,15 @@ class WM_MT_splash_quick_setup(Menu):
col = split.column()
col.operator(
"preferences.copy_prev",
text=iface_("Load Blender %d.%d Settings", "Operator") % old_version,
icon='DUPLICATE',
text=iface_("Import Blender %d.%d Preferences", "Operator") % old_version,
icon='NONE',
translate=False,
)
col.operator(
"wm.url_open", text="See What's New...", icon='URL',
).url = "https://developer.blender.org/docs/release_notes/%d.%d" % bpy.app.version[:2]
col.separator(factor=2.0)
layout.separator()
layout.separator(type='LINE')
if can_import:
layout.label(text="Create New Settings")
layout.label(text="Create New Preferences")
else:
layout.label(text="Quick Setup")
@ -3261,14 +3259,22 @@ class WM_MT_splash_quick_setup(Menu):
if bpy.app.build_options.international:
prefs = context.preferences
col.prop(prefs.view, "language")
col.separator()
# Themes.
sub = col.column(heading="Theme")
label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label
if label == "Presets":
label = "Blender Dark"
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
col.separator()
# Shortcuts.
wm = context.window_manager
kc = wm.keyconfigs.active
kc_prefs = kc.preferences
sub = col.column(heading="Shortcuts")
sub = col.column(heading="Keymap")
text = bpy.path.display_name(kc.name)
if not text:
text = "Blender"
@ -3276,25 +3282,19 @@ class WM_MT_splash_quick_setup(Menu):
has_select_mouse = hasattr(kc_prefs, "select_mouse")
if has_select_mouse:
col.row().prop(kc_prefs, "select_mouse", text="Select With", expand=True)
col.row().prop(kc_prefs, "select_mouse", text="Mouse Select", expand=True)
has_spacebar_action = hasattr(kc_prefs, "spacebar_action")
if has_spacebar_action:
col.row().prop(kc_prefs, "spacebar_action", text="Spacebar")
col.row().prop(kc_prefs, "spacebar_action", text="Spacebar Action")
# Themes.
col.separator()
sub = col.column(heading="Theme")
label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label
if label == "Presets":
label = "Blender Dark"
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
sub = col.column()
sub.separator(factor=2)
if can_import:
sub.label()
sub.operator("wm.save_userpref", text="Save New Settings", icon='CHECKMARK')
sub.operator("wm.save_userpref", text="Save New Preferences", icon='NONE')
else:
sub.label()
sub.operator("wm.save_userpref", text="Continue")
layout.separator(factor=2.0)

View File

@ -182,6 +182,9 @@ class ARMATURE_MT_collection_tree_context_menu(Menu):
layout.operator("armature.collection_select", text="Select Bones")
layout.operator("armature.collection_deselect", text="Deselect Bones")
layout.separator()
layout.operator("UI_OT_view_item_rename", text="Rename")
class DATA_PT_iksolver_itasc(ArmatureButtonsPanel, Panel):
bl_label = "Inverse Kinematics"

View File

@ -70,7 +70,7 @@ class GreasePencilDisplayPanel:
def poll(cls, context):
ob = context.active_object
brush = context.tool_settings.gpencil_paint.brush
if ob and ob.type == 'GPENCIL' and brush:
if ob and ob.type in {'GPENCIL', 'GREASE_PENCIL'} and brush:
return True
return False

View File

@ -84,6 +84,8 @@ class UnifiedPaintPanel:
return tool_settings.curves_sculpt
elif mode == 'PAINT_GREASE_PENCIL':
return tool_settings.gpencil_paint
elif mode == 'SCULPT_GREASE_PENCIL':
return tool_settings.gpencil_sculpt_paint
return None
@staticmethod
@ -906,6 +908,11 @@ def brush_shared_settings(layout, context, brush, popover=False):
size = True
strength = True
# Grease Pencil #
if mode == 'SCULPT_GREASE_PENCIL':
size = True
strength = True
### Draw settings. ###
ups = context.scene.tool_settings.unified_paint_settings
@ -1053,6 +1060,17 @@ def brush_settings_advanced(layout, context, brush, popover=False):
col.prop(brush, "use_original_plane", text="Plane")
layout.separator()
elif mode == 'SCULPT_GREASE_PENCIL':
tool = brush.gpencil_sculpt_tool
gp_settings = brush.gpencil_settings
if tool in {'SMOOTH', 'RANDOMIZE'}:
col = layout.column(heading="Affect", align=True)
col.prop(gp_settings, "use_edit_position", text="Position")
col.prop(gp_settings, "use_edit_strength", text="Strength")
col.prop(gp_settings, "use_edit_thickness", text="Thickness")
col.prop(gp_settings, "use_edit_uv", text="UV")
# 3D and 2D Texture Paint.
elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}:
capabilities = brush.image_paint_capabilities

View File

@ -2460,6 +2460,37 @@ class _defs_gpencil_sculpt:
)
class _defs_grease_pencil_sculpt:
@staticmethod
def poll_select_mask(context):
if context is None:
return True
ob = context.active_object
tool_settings = context.scene.tool_settings
return (
ob is not None and
ob.type in {'GPENCIL', 'GREASE_PENCIL'} and (
tool_settings.use_gpencil_select_mask_point or
tool_settings.use_gpencil_select_mask_stroke or
tool_settings.use_gpencil_select_mask_segment
)
)
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="ops.gpencil.sculpt_",
type=bpy.types.Brush,
# Uses GPv2 tool settings
attr="gpencil_sculpt_tool",
tooldef_keywords=dict(
operator="grease_pencil.sculpt_paint",
),
)
class _defs_gpencil_weight:
@staticmethod
@ -3159,6 +3190,16 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
*_tools_annotate,
],
'SCULPT_GREASE_PENCIL': [
_defs_grease_pencil_sculpt.generate_from_brushes,
None,
*_tools_annotate,
lambda context: (
VIEW3D_PT_tools_active._tools_gpencil_select
if _defs_grease_pencil_sculpt.poll_select_mask(context)
else ()
),
],
'PAINT_TEXTURE': [
_defs_texture_paint.generate_from_brushes,
None,

View File

@ -120,6 +120,14 @@ class VIEW3D_HT_tool_header(Header):
if tool in {'SMOOTH', 'RANDOMIZE'}:
layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover")
layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance")
elif tool_mode == 'SCULPT_GREASE_PENCIL':
if is_valid_context:
brush = context.tool_settings.gpencil_sculpt_paint.brush
if brush:
tool = brush.gpencil_sculpt_tool
if tool in {'SMOOTH', 'RANDOMIZE'}:
layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover")
layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance")
elif tool_mode == 'WEIGHT_GPENCIL':
if is_valid_context:
layout.popover("VIEW3D_PT_tools_grease_pencil_weight_appearance")
@ -417,6 +425,59 @@ class _draw_tool_settings_context_mode:
return True
@staticmethod
def SCULPT_GREASE_PENCIL(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
return False
paint = context.tool_settings.gpencil_sculpt_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
if brush is None:
return False
tool_settings = context.tool_settings
capabilities = brush.sculpt_capabilities
ups = tool_settings.unified_paint_settings
size = "size"
size_owner = ups if ups.use_unified_size else brush
if size_owner.use_locked_size == 'SCENE':
size = "unprojected_radius"
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
size,
pressure_name="use_pressure_size",
unified_name="use_unified_size",
text="Radius",
slider=True,
header=True,
)
# strength, use_strength_pressure
pressure_name = "use_pressure_strength" if capabilities.has_strength_pressure else None
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"strength",
pressure_name=pressure_name,
unified_name="use_unified_strength",
text="Strength",
header=True,
)
# direction
if not capabilities.has_direction:
layout.row().prop(brush, "direction", expand=True, text="")
return True
@staticmethod
def WEIGHT_GPENCIL(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
@ -1132,7 +1193,7 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_select_paint_mask")
elif mesh.use_paint_mask_vertex and mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX'}:
layout.menu("VIEW3D_MT_select_paint_mask_vertex")
elif mode_string not in {'SCULPT', 'SCULPT_CURVES', 'PAINT_GREASE_PENCIL'}:
elif mode_string not in {'SCULPT', 'SCULPT_CURVES', 'PAINT_GREASE_PENCIL', 'SCULPT_GREASE_PENCIL'}:
layout.menu("VIEW3D_MT_select_%s" % mode_string.lower())
if gp_edit:
@ -1175,6 +1236,7 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_edit_curve_ctrlpoints")
layout.menu("VIEW3D_MT_edit_curve_segments")
elif mode_string in {'EDIT_CURVES', 'EDIT_POINT_CLOUD'}:
layout.menu("VIEW3D_MT_edit_curves_control_points")
layout.menu("VIEW3D_MT_edit_curves_segments")
layout.template_node_operator_asset_root_items()
elif mode_string == 'EDIT_GREASE_PENCIL':
@ -1182,7 +1244,7 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_edit_greasepencil_point")
elif obj:
if mode_string not in {'PAINT_TEXTURE', 'SCULPT_CURVES'}:
if mode_string not in {'PAINT_TEXTURE', 'SCULPT_CURVES', 'SCULPT_GREASE_PENCIL'}:
layout.menu("VIEW3D_MT_%s" % mode_string.lower())
if mode_string == 'SCULPT':
layout.menu("VIEW3D_MT_mask")
@ -5917,6 +5979,15 @@ class VIEW3D_MT_edit_curves(Menu):
layout.template_node_operator_asset_menu_items(catalog_path=self.bl_label)
class VIEW3D_MT_edit_curves_control_points(Menu):
bl_label = "Control Points"
def draw(self, _context):
layout = self.layout
layout.operator_menu_enum("curves.handle_type_set", "type")
class VIEW3D_MT_edit_curves_segments(Menu):
bl_label = "Segments"
@ -9070,6 +9141,7 @@ classes = (
VIEW3D_MT_edit_gpencil_transform,
VIEW3D_MT_edit_curves,
VIEW3D_MT_edit_curves_segments,
VIEW3D_MT_edit_curves_control_points,
VIEW3D_MT_edit_pointcloud,
VIEW3D_MT_object_mode_pie,
VIEW3D_MT_view_pie,

View File

@ -624,8 +624,11 @@ class VIEW3D_PT_slots_paint_canvas(SelectPaintSlotHelper, View3DPanel, Panel):
if mat and mat.texture_paint_images and mat.texture_paint_slots:
label = mat.texture_paint_slots[mat.paint_active_slot].name
elif paint.canvas_source == 'COLOR_ATTRIBUTE':
label = (me.color_attributes.active_color.name if me.color_attributes.active_color
else iface_("Color Attribute"))
active_color = me.color_attributes.active_color
label = (
active_color.name if active_color else
iface_("Color Attribute")
)
elif paint.canvas_image:
label = paint.canvas_image.name
@ -640,8 +643,9 @@ class VIEW3D_PT_slots_color_attributes(Panel):
def draw_header(self, context):
me = context.object.data
active_color = me.color_attributes.active_color
self.bl_label = (
iface_("%s") % (me.color_attributes.active_color.name) if me.color_attributes.active_color else
active_color.name if active_color else
iface_("Color Attributes")
)
@ -682,7 +686,7 @@ class VIEW3D_PT_slots_vertex_groups(Panel):
ob = context.object
groups = ob.vertex_groups
self.bl_label = (
iface_("%s") % (groups.active.name) if groups and groups.active else
groups.active.name if groups and groups.active else
iface_("Vertex Groups")
)

View File

@ -10,7 +10,10 @@ It is not meant for any particular use, just to have *something* in the UI.
import threading
import bpy
from bpy.types import Context, Panel, WindowManager
from bpy.types import (
Panel,
WindowManager,
)
from bpy.props import PointerProperty
@ -21,15 +24,15 @@ class VIEW3D_PT_animation_layers(Panel):
bl_label = "Baklava"
@classmethod
def poll(cls, context: Context) -> bool:
def poll(cls, context):
return context.preferences.experimental.use_animation_baklava and context.object
def draw(self, context: Context) -> None:
def draw(self, context) -> None:
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
# FIXME: this should be done in response to a messagebus callback, notifier, whatnot.
# FIXME: this should be done in response to a message-bus callback, notifier, whatnot.
adt = context.object.animation_data
with _wm_selected_animation_lock:
if adt:
@ -38,7 +41,7 @@ class VIEW3D_PT_animation_layers(Panel):
context.window_manager.selected_animation = None
col = layout.column()
# This has to go via an auxillary property, as assigning an Animation
# This has to go via an auxiliary property, as assigning an Animation
# data-block should be possible even when `context.object.animation_data`
# is `None`, and thus its `animation` property does not exist.
col.template_ID(context.window_manager, 'selected_animation')
@ -66,7 +69,7 @@ class VIEW3D_PT_animation_layers(Panel):
for layer_idx, layer in reversed(list(enumerate(anim.layers))):
layerbox = layout.box()
col = layerbox.column(align=True)
col.prop(layer, "name", text=f"Layer {layer_idx+1}:")
col.prop(layer, "name", text="Layer %d:" % (layer_idx + 1))
col.prop(layer, "influence")
col.prop(layer, "mix_mode")
@ -78,25 +81,25 @@ classes = (
_wm_selected_animation_lock = threading.Lock()
def _wm_selected_animation_update(self: WindowManager, context: Context) -> None:
def _wm_selected_animation_update(wm, context):
# Avoid responding to changes written by the panel above.
lock_ok = _wm_selected_animation_lock.acquire(blocking=False)
if not lock_ok:
return
try:
if self.selected_animation is None and context.object.animation_data is None:
if wm.selected_animation is None and context.object.animation_data is None:
return
adt = context.object.animation_data_create()
if adt.animation == self.selected_animation:
if adt.animation == wm.selected_animation:
# Avoid writing to the property when the new value hasn't changed.
return
adt.animation = self.selected_animation
adt.animation = wm.selected_animation
finally:
_wm_selected_animation_lock.release()
def register_props() -> None:
def register_props():
# Put behind a `try` because it won't exist when Blender is built without
# experimental features.
try:

View File

@ -184,8 +184,8 @@ class Strip : public ::AnimationStrip {
*
* The reason is that various functions will assume that the `Strip` is actually a down-cast
* instance of another strip class, and that `Strip::type()` will say which type. To avoid having
* to explcitly deal with an 'invalid' type everywhere, creating a `Strip` directly is simply not
* allowed.
* to explicitly deal with an 'invalid' type everywhere, creating a `Strip` directly is simply
* not allowed.
*/
Strip() = delete;
@ -443,14 +443,14 @@ static_assert(sizeof(ChannelBag) == sizeof(::AnimationChannelBag),
*
* - By binding handle.
* - By fallback string.
* - By the ID's name (matching agains the binding name).
* - By the ID's name (matching against the binding name).
* - If the above do not find a suitable binding, the animated ID will not
* receive any animation and the calller is responsible for creating a binding
* receive any animation and the caller is responsible for creating a binding
* and assigning it.
*
* \return `false` if the assignment was not possible (for example the ID is of a type that cannot
* be animated). If the above fall-through case of "no binding found" is reached, this function
* will still return `true` as the Animation was succesfully assigned.
* will still return `true` as the Animation was successfully assigned.
*/
bool assign_animation(Animation &anim, ID &animated_id);

View File

@ -10,6 +10,7 @@
#pragma once
#include <array>
#include <string>
#include "BLI_array.hh"
@ -48,7 +49,7 @@ class CombinedKeyingResult {
private:
/* The index to the array maps a `SingleKeyingResult` to the number of times this result has
* occurred. */
Array<int> result_counter;
std::array<int, size_t(SingleKeyingResult::_KEYING_RESULT_MAX)> result_counter;
public:
CombinedKeyingResult();

View File

@ -186,7 +186,7 @@ Binding *Animation::binding_for_handle(const binding_handle_t handle)
const Binding *Animation::binding_for_handle(const binding_handle_t handle) const
{
/* TODO: implement hashmap lookup. */
/* TODO: implement hash-map lookup. */
for (const Binding *binding : bindings()) {
if (binding->handle == handle) {
return binding;
@ -236,7 +236,7 @@ void Animation::binding_name_define(Binding &binding, const StringRefNull new_na
void Animation::binding_name_propagate(Main &bmain, const Binding &binding)
{
/* Just loop over all animatable IDs in the main dataabase. */
/* Just loop over all animatable IDs in the main database. */
ListBase *lb;
ID *id;
FOREACH_MAIN_LISTBASE_BEGIN (&bmain, lb) {

View File

@ -33,7 +33,7 @@ class AnimationLayersTest : public testing::Test {
static void SetUpTestSuite()
{
/* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialised properly. */
/* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
CLG_init();
/* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
@ -248,7 +248,7 @@ TEST_F(AnimationLayersTest, rename_binding)
EXPECT_STREQ("New Binding Name", out_cube.name);
/* At this point the binding name will not have been copied to the cube
* AnimData. However, I don't want to test for that here, as it's not exactly
* desirable behaviour, but more of a side-effect of the current
* desirable behavior, but more of a side-effect of the current
* implementation. */
anim->binding_name_propagate(*bmain, out_cube);

View File

@ -1207,12 +1207,11 @@ bool armature_bonecoll_is_child_of(const bArmature *armature,
const int potential_child_index)
{
/* Check for roots, before we try and access collection_array[-1]. */
const bool is_root = armature_bonecoll_is_root(armature, potential_child_index);
if (is_root) {
if (armature_bonecoll_is_root(armature, potential_child_index)) {
return potential_parent_index == -1;
}
if (potential_parent_index < 0) {
return is_root;
return false;
}
const BoneCollection *potential_parent = armature->collection_array[potential_parent_index];

View File

@ -916,7 +916,7 @@ TEST_F(ArmatureBoneCollections, bcoll_move_to_parent__root_unroot)
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[2]->name); // Became a root.
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[2]->name); /* Became a root. */
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
@ -941,10 +941,10 @@ TEST_F(ArmatureBoneCollections, bcoll_move_to_parent__root_unroot)
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[1]->name); // Actually a root.
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[1]->name); /* Actually a root. */
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[4]->name); // Became a child.
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[4]->name); /* Became a child. */
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);

View File

@ -50,7 +50,6 @@ namespace blender::animrig {
CombinedKeyingResult::CombinedKeyingResult()
{
result_counter = Array<int>(int(SingleKeyingResult::_KEYING_RESULT_MAX));
result_counter.fill(0);
}

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 12
#define BLENDER_FILE_SUBVERSION 14
/* 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

@ -141,8 +141,9 @@ enum eContextObjectMode {
CTX_MODE_VERTEX_GPENCIL_LEGACY,
CTX_MODE_SCULPT_CURVES,
CTX_MODE_PAINT_GREASE_PENCIL,
CTX_MODE_SCULPT_GREASE_PENCIL,
};
#define CTX_MODE_NUM (CTX_MODE_PAINT_GREASE_PENCIL + 1)
#define CTX_MODE_NUM (CTX_MODE_SCULPT_GREASE_PENCIL + 1)
/* Context */

View File

@ -298,16 +298,21 @@ struct LayerTransformData {
* frame indices, and the values of the map are the destination frame indices. */
Map<int, int> frames_destination;
/* Copy of the layer frames map. This allows to display the transformation while running, without
* removing any drawing. */
Map<int, GreasePencilFrame> frames_copy;
/* Copy of the layer frames, stored in two separate maps :
* - frames_static contains the frames not affected by the transformation,
* - frames_transformed contains the frames affected by the transformation.
* This allows to display the transformation while running, without removing any drawing.
*/
Map<int, GreasePencilFrame> frames_static;
Map<int, GreasePencilFrame> frames_transformed;
/* Map containing the duration (in frames) for each frame in the layer that has a fixed duration,
* i.e. each frame that is not an implicit hold. */
Map<int, int> frames_duration;
/* Temporary copy of duplicated frames before we decide on a place to insert them.
* Used in the move+duplicate operator. */
Map<int, GreasePencilFrame> temp_frames_buffer;
Map<int, GreasePencilFrame> duplicated_frames_buffer;
FrameTransformationStatus status{TRANS_CLEAR};
};
@ -509,6 +514,8 @@ class Layer : public ::GreasePencilLayer {
SortedKeysIterator remove_leading_null_frames_in_range(SortedKeysIterator begin,
SortedKeysIterator end);
float4x4 parent_inverse() const;
/**
* The local transform of the layer (in layer space, not object space).
*/

View File

@ -84,6 +84,7 @@ extern const uchar PAINT_CURSOR_VERTEX_PAINT[3];
extern const uchar PAINT_CURSOR_WEIGHT_PAINT[3];
extern const uchar PAINT_CURSOR_TEXTURE_PAINT[3];
extern const uchar PAINT_CURSOR_SCULPT_CURVES[3];
extern const uchar PAINT_CURSOR_SCULPT_GREASE_PENCIL[3];
enum class PaintMode : int8_t {
Sculpt = 0,
@ -102,9 +103,11 @@ enum class PaintMode : int8_t {
WeightGPencil = 9,
/** Curves. */
SculptCurves = 10,
/** Grease Pencil. */
SculptGreasePencil = 11,
/** Keep last. */
Invalid = 11,
Invalid = 12,
};
#define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PaintMode::SculptUV)

View File

@ -1191,7 +1191,12 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit,
return CTX_MODE_EDIT_GPENCIL_LEGACY;
}
if (object_mode & OB_MODE_SCULPT_GPENCIL_LEGACY) {
return CTX_MODE_SCULPT_GPENCIL_LEGACY;
if (ob->type == OB_GPENCIL_LEGACY) {
return CTX_MODE_SCULPT_GPENCIL_LEGACY;
}
if (ob->type == OB_GREASE_PENCIL) {
return CTX_MODE_SCULPT_GREASE_PENCIL;
}
}
if (object_mode & OB_MODE_WEIGHT_GPENCIL_LEGACY) {
return CTX_MODE_WEIGHT_GPENCIL_LEGACY;
@ -1248,6 +1253,7 @@ static const char *data_mode_strings[] = {
"greasepencil_vertex",
"curves_sculpt",
"grease_pencil_paint",
"grease_pencil_sculpt",
nullptr,
};
BLI_STATIC_ASSERT(ARRAY_SIZE(data_mode_strings) == CTX_MODE_NUM + 1,

View File

@ -435,6 +435,7 @@ void BKE_crazyspace_build_sculpt(Depsgraph *depsgraph,
VirtualModifierData virtual_modifier_data;
Object object_eval;
crazyspace_init_object_for_eval(depsgraph, object, &object_eval);
BLI_SCOPED_DEFER([&]() { MEM_delete(object_eval.runtime); });
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(&object_eval,
&virtual_modifier_data);
const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)};

View File

@ -1966,8 +1966,12 @@ void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool
void BKE_fcurve_deduplicate_keys(FCurve *fcu)
{
if (fcu->totvert < 2) {
return;
}
BLI_assert_msg(fcu->bezt, "this function only works with regular (non-sampled) FCurves");
if (fcu->totvert < 2 || fcu->bezt == nullptr) {
if (fcu->bezt == nullptr) {
return;
}

View File

@ -911,6 +911,7 @@ Layer::Layer()
this->parent = nullptr;
this->parsubstr = nullptr;
unit_m4(this->parentinv);
zero_v3(this->translation);
zero_v3(this->rotation);
@ -944,6 +945,7 @@ Layer::Layer(const Layer &other) : Layer()
this->parent = other.parent;
this->set_parent_bone_name(other.parsubstr);
copy_m4_m4(this->parentinv, other.parentinv);
copy_v3_v3(this->translation, other.translation);
copy_v3_v3(this->rotation, other.rotation);
@ -1225,7 +1227,7 @@ float4x4 Layer::to_world_space(const Object &object) const
return object.object_to_world() * this->local_transform();
}
const Object &parent = *this->parent;
return this->parent_to_world(parent) * this->local_transform();
return this->parent_to_world(parent) * this->parent_inverse() * this->local_transform();
}
float4x4 Layer::to_object_space(const Object &object) const
@ -1234,7 +1236,8 @@ float4x4 Layer::to_object_space(const Object &object) const
return this->local_transform();
}
const Object &parent = *this->parent;
return object.world_to_object() * this->parent_to_world(parent) * this->local_transform();
return object.world_to_object() * this->parent_to_world(parent) * this->parent_inverse() *
this->local_transform();
}
StringRefNull Layer::parent_bone_name() const
@ -1263,6 +1266,11 @@ float4x4 Layer::parent_to_world(const Object &parent) const
return parent_object_to_world;
}
float4x4 Layer::parent_inverse() const
{
return float4x4_view(this->parentinv);
}
float4x4 Layer::local_transform() const
{
return math::from_loc_rot_scale<float4x4, math::EulerXYZ>(
@ -2225,91 +2233,97 @@ bool GreasePencil::remove_frames(blender::bke::greasepencil::Layer &layer,
return false;
}
static void remove_drawings_unchecked(GreasePencil &grease_pencil,
Span<int64_t> sorted_indices_to_remove)
{
using namespace blender::bke::greasepencil;
if (grease_pencil.drawing_array_num == 0 || sorted_indices_to_remove.is_empty()) {
return;
}
const int64_t drawings_to_remove = sorted_indices_to_remove.size();
const blender::IndexRange last_drawings_range(
grease_pencil.drawings().size() - drawings_to_remove, drawings_to_remove);
/* We keep track of the next available index (for swapping) by iterating from the end and
* skipping over drawings that are already in the range to be removed. */
auto next_available_index = last_drawings_range.last();
auto greatest_index_to_remove_it = std::rbegin(sorted_indices_to_remove);
auto get_next_available_index = [&]() {
while (next_available_index == *greatest_index_to_remove_it) {
greatest_index_to_remove_it = std::prev(greatest_index_to_remove_it);
next_available_index--;
}
return next_available_index;
};
/* Move the drawings to be removed to the end of the array by swapping the pointers. Make sure to
* remap any frames pointing to the drawings being swapped. */
for (const int64_t index_to_remove : sorted_indices_to_remove) {
if (index_to_remove >= last_drawings_range.first()) {
/* This drawing and all the next drawings are already in the range to be removed. */
break;
}
const int64_t swap_index = get_next_available_index();
/* Remap the drawing_index for frames that point to the drawing to be swapped with. */
for (Layer *layer : grease_pencil.layers_for_write()) {
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();
}
}
}
/* Swap the pointers to the drawings in the drawing array. */
std::swap(grease_pencil.drawing_array[index_to_remove],
grease_pencil.drawing_array[swap_index]);
next_available_index--;
}
/* Free the last drawings. */
for (const int64_t drawing_index : last_drawings_range) {
GreasePencilDrawingBase *drawing_base_to_remove = grease_pencil.drawing(drawing_index);
switch (drawing_base_to_remove->type) {
case GP_DRAWING: {
GreasePencilDrawing *drawing_to_remove = reinterpret_cast<GreasePencilDrawing *>(
drawing_base_to_remove);
MEM_delete(&drawing_to_remove->wrap());
break;
}
case GP_DRAWING_REFERENCE: {
GreasePencilDrawingReference *drawing_reference_to_remove =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_base_to_remove);
MEM_delete(&drawing_reference_to_remove->wrap());
break;
}
}
}
/* Shrink drawing array. */
shrink_array<GreasePencilDrawingBase *>(
&grease_pencil.drawing_array, &grease_pencil.drawing_array_num, drawings_to_remove);
}
void GreasePencil::remove_drawings_with_no_users()
{
using namespace blender;
Vector<int64_t> drawings_to_be_removed;
for (const int64_t drawing_i : this->drawings().index_range()) {
GreasePencilDrawingBase *drawing_base = this->drawing(drawing_i);
using namespace blender::bke::greasepencil;
/* Compress the drawings array by finding unused drawings.
* In every step two indices are found:
* - The next unused drawing from the start
* - The last used drawing from the end
* These two drawings are then swapped. Rinse and repeat until both iterators meet somewhere in
* the middle. At this point the drawings array is fully compressed.
* Then the drawing indices in frame data are remapped. */
const MutableSpan<GreasePencilDrawingBase *> drawings = this->drawings();
if (drawings.is_empty()) {
return;
}
auto is_drawing_used = [&](const int drawing_index) {
GreasePencilDrawingBase *drawing_base = drawings[drawing_index];
/* Note: GreasePencilDrawingReference does not have a user count currently, but should
* eventually be counted like GreasePencilDrawing. */
if (drawing_base->type != GP_DRAWING) {
continue;
return false;
}
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
if (!drawing->wrap().has_users()) {
drawings_to_be_removed.append(drawing_i);
return drawing->wrap().has_users();
};
/* Index map to remap drawing indices in frame data.
* Index -1 indicates that the drawing has not been moved. */
constexpr const int unchanged_index = -1;
Array<int> drawing_index_map(drawings.size(), unchanged_index);
int first_unused_drawing = -1;
int last_used_drawing = drawings.size();
/* Advance head and tail iterators to the next unused/used drawing respectively.
* Returns true if an index pair was found that needs to be swapped. */
auto find_next_swap_index = [&]() -> bool {
do {
++first_unused_drawing;
} while (first_unused_drawing < last_used_drawing && is_drawing_used(first_unused_drawing));
do {
--last_used_drawing;
} while (first_unused_drawing < last_used_drawing && !is_drawing_used(last_used_drawing));
return first_unused_drawing < last_used_drawing;
};
while (find_next_swap_index()) {
/* Found two valid iterators, now swap drawings. */
std::swap(drawings[first_unused_drawing], drawings[last_used_drawing]);
drawing_index_map[last_used_drawing] = first_unused_drawing;
}
/* Tail range of unused drawings that can be removed. */
const IndexRange drawings_to_remove = drawings.index_range().drop_front(last_used_drawing + 1);
if (drawings_to_remove.is_empty()) {
return;
}
/* Free the unused drawings. */
for (const int i : drawings_to_remove) {
GreasePencilDrawingBase *unused_drawing_base = drawings[i];
switch (unused_drawing_base->type) {
case GP_DRAWING: {
auto *unused_drawing = reinterpret_cast<GreasePencilDrawing *>(unused_drawing_base);
MEM_delete(&unused_drawing->wrap());
break;
}
case GP_DRAWING_REFERENCE: {
auto *unused_drawing_ref = reinterpret_cast<GreasePencilDrawingReference *>(
unused_drawing_base);
MEM_delete(&unused_drawing_ref->wrap());
break;
}
}
}
shrink_array<GreasePencilDrawingBase *>(
&this->drawing_array, &this->drawing_array_num, drawings_to_remove.size());
/* Remap drawing indices in frame data. */
for (Layer *layer : this->layers_for_write()) {
for (auto [key, value] : layer->frames_for_write().items()) {
const int new_drawing_index = drawing_index_map[value.drawing_index];
if (new_drawing_index != unchanged_index) {
value.drawing_index = new_drawing_index;
layer->tag_frames_map_changed();
}
}
}
remove_drawings_unchecked(*this, drawings_to_be_removed.as_span());
}
void GreasePencil::update_drawing_users_for_layer(const blender::bke::greasepencil::Layer &layer)

View File

@ -586,6 +586,7 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b
new_layer.parent = gpl->parent;
new_layer.set_parent_bone_name(gpl->parsubstr);
copy_m4_m4(new_layer.parentinv, gpl->inverse);
copy_v3_v3(new_layer.translation, gpl->location);
copy_v3_v3(new_layer.rotation, gpl->rotation);

View File

@ -643,13 +643,13 @@ static float (*mask_spline_feather_differentiated_points_with_resolution__double
/* before we transform verts */
len_base = len_v2v2(bezt_prev->vec[1], bezt_curr->vec[1]);
// add_v2_v2(bezt_prev->vec[0], point_prev_n); // not needed
// add_v2_v2(bezt_prev->vec[0], point_prev_n); /* Not needed. */
add_v2_v2(bezt_prev->vec[1], point_prev_n);
add_v2_v2(bezt_prev->vec[2], point_prev_n);
add_v2_v2(bezt_curr->vec[0], point_curr_n);
add_v2_v2(bezt_curr->vec[1], point_curr_n);
// add_v2_v2(bezt_curr->vec[2], point_curr_n); // not needed
// add_v2_v2(bezt_curr->vec[2], point_curr_n); /* Not needed. */
len_feather = len_v2v2(bezt_prev->vec[1], bezt_curr->vec[1]);

View File

@ -6,10 +6,11 @@
* \ingroup bke
*/
#include "BLI_map.hh"
#include "BLI_array_utils.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
#include "BKE_customdata.hh"
@ -28,12 +29,12 @@ static uint64_t edge_hash_2(const OrderedEdge &edge)
return edge.v_low;
}
/* The map first contains an edge pointer and later an index. */
union OrigEdgeOrIndex {
const int2 *original_edge;
int index;
};
using EdgeMap = Map<OrderedEdge, OrigEdgeOrIndex>;
using EdgeMap = VectorSet<OrderedEdge,
DefaultProbingStrategy,
DefaultHash<OrderedEdge>,
DefaultEquality<OrderedEdge>,
SimpleVectorSetSlot<OrderedEdge, int>,
GuardedAllocator>;
static void reserve_hash_maps(const Mesh &mesh,
const bool keep_existing_edges,
@ -52,11 +53,11 @@ static void add_existing_edges_to_hash_maps(const Mesh &mesh,
const Span<int2> edges = mesh.edges();
threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
const int task_index = &edge_map - edge_maps.data();
for (const int2 &edge : edges) {
const OrderedEdge ordered_edge(edge[0], edge[1]);
for (const int2 edge : edges) {
const OrderedEdge ordered_edge(edge);
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.add_new(ordered_edge, {&edge});
edge_map.add(ordered_edge);
}
}
});
@ -76,11 +77,11 @@ static void add_face_edges_to_hash_maps(const Mesh &mesh,
const int vert = corner_verts[corner];
const int vert_prev = corner_verts[bke::mesh::face_corner_prev(face, corner)];
/* Can only be the same when the mesh data is invalid. */
if (vert_prev != vert) {
if (LIKELY(vert_prev != vert)) {
const OrderedEdge ordered_edge(vert_prev, vert);
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.lookup_or_add(ordered_edge, {nullptr});
edge_map.add(ordered_edge);
}
}
}
@ -89,35 +90,17 @@ static void add_face_edges_to_hash_maps(const Mesh &mesh,
}
static void serialize_and_initialize_deduplicated_edges(MutableSpan<EdgeMap> edge_maps,
const OffsetIndices<int> edge_offsets,
MutableSpan<int2> new_edges)
{
/* All edges are distributed in the hash tables now. They have to be serialized into a single
* array below. To be able to parallelize this, we have to compute edge index offsets for each
* map. */
Array<int> edge_sizes(edge_maps.size() + 1);
for (const int i : edge_maps.index_range()) {
edge_sizes[i] = edge_maps[i].size();
}
const OffsetIndices<int> edge_offsets = offset_indices::accumulate_counts_to_offsets(edge_sizes);
threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
const int task_index = &edge_map - edge_maps.data();
int new_edge_index = edge_offsets[task_index].first();
for (EdgeMap::MutableItem item : edge_map.items()) {
int2 &new_edge = new_edges[new_edge_index];
const int2 *orig_edge = item.value.original_edge;
if (orig_edge != nullptr) {
/* Copy values from original edge. */
new_edge = *orig_edge;
}
else {
/* Initialize new edge. */
new_edge = int2(item.key.v_low, item.key.v_high);
}
item.value.index = new_edge_index;
new_edge_index++;
if (edge_offsets[task_index].is_empty()) {
return;
}
MutableSpan<int2> result_edges = new_edges.slice(edge_offsets[task_index]);
result_edges.copy_from(edge_map.as_span().cast<int2>());
});
}
@ -125,6 +108,7 @@ static void update_edge_indices_in_face_loops(const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<EdgeMap> edge_maps,
const uint32_t parallel_mask,
const OffsetIndices<int> edge_offsets,
MutableSpan<int> corner_edges)
{
threading::parallel_for(faces.index_range(), 100, [&](IndexRange range) {
@ -133,20 +117,19 @@ static void update_edge_indices_in_face_loops(const OffsetIndices<int> faces,
for (const int corner : face) {
const int vert = corner_verts[corner];
const int vert_prev = corner_verts[bke::mesh::face_corner_next(face, corner)];
int edge_index;
if (vert_prev != vert) {
const OrderedEdge ordered_edge(vert_prev, vert);
/* Double lookup: First find the map that contains the edge, then lookup the edge. */
const EdgeMap &edge_map = edge_maps[parallel_mask & edge_hash_2(ordered_edge)];
edge_index = edge_map.lookup(ordered_edge).index;
}
else {
if (UNLIKELY(vert == vert_prev)) {
/* This is an invalid edge; normally this does not happen in Blender,
* but it can be part of an imported mesh with invalid geometry. See
* #76514. */
edge_index = 0;
corner_edges[corner] = 0;
continue;
}
const OrderedEdge ordered_edge(vert_prev, vert);
const int task_index = parallel_mask & edge_hash_2(ordered_edge);
const EdgeMap &edge_map = edge_maps[task_index];
const int edge_i = edge_map.index_of(ordered_edge);
const int edge_index = edge_offsets[task_index][edge_i];
corner_edges[corner] = edge_index;
}
}
@ -170,6 +153,24 @@ static void clear_hash_tables(MutableSpan<EdgeMap> edge_maps)
threading::parallel_for_each(edge_maps, [](EdgeMap &edge_map) { edge_map.clear_and_shrink(); });
}
static void deselect_known_edges(const OffsetIndices<int> edge_offsets,
const Span<EdgeMap> edge_maps,
const uint32_t parallel_mask,
const Span<int2> known_edges,
MutableSpan<bool> selection)
{
threading::parallel_for(known_edges.index_range(), 2048, [&](const IndexRange range) {
for (const int2 original_edge : known_edges.slice(range)) {
const OrderedEdge ordered_edge(original_edge);
const int task_index = parallel_mask & edge_hash_2(ordered_edge);
const EdgeMap &edge_map = edge_maps[task_index];
const int edge_i = edge_map.index_of(ordered_edge);
const int edge_index = edge_offsets[task_index][edge_i];
selection[edge_index] = false;
}
});
}
} // namespace calc_edges
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, const bool select_new_edges)
@ -188,24 +189,35 @@ void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, const bool select_new
}
calc_edges::add_face_edges_to_hash_maps(mesh, parallel_mask, edge_maps);
/* Compute total number of edges. */
int new_totedge = 0;
for (const calc_edges::EdgeMap &edge_map : edge_maps) {
new_totedge += edge_map.size();
Array<int> edge_sizes(edge_maps.size() + 1);
for (const int i : edge_maps.index_range()) {
edge_sizes[i] = edge_maps[i].size();
}
const OffsetIndices<int> edge_offsets = offset_indices::accumulate_counts_to_offsets(edge_sizes);
/* Create new edges. */
MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.add<int>(".corner_edge", AttrDomain::Corner, AttributeInitConstruct());
MutableSpan<int2> new_edges(MEM_cnew_array<int2>(new_totedge, __func__), new_totedge);
calc_edges::serialize_and_initialize_deduplicated_edges(edge_maps, new_edges);
calc_edges::update_edge_indices_in_face_loops(
mesh.faces(), mesh.corner_verts(), edge_maps, parallel_mask, mesh.corner_edges_for_write());
MutableSpan<int2> new_edges(MEM_cnew_array<int2>(edge_offsets.total_size(), __func__),
edge_offsets.total_size());
calc_edges::serialize_and_initialize_deduplicated_edges(edge_maps, edge_offsets, new_edges);
calc_edges::update_edge_indices_in_face_loops(mesh.faces(),
mesh.corner_verts(),
edge_maps,
parallel_mask,
edge_offsets,
mesh.corner_edges_for_write());
Array<int2> original_edges;
if (keep_existing_edges && select_new_edges) {
original_edges.reinitialize(mesh.edges_num);
array_utils::copy(mesh.edges(), original_edges.as_mutable_span());
}
/* Free old CustomData and assign new one. */
CustomData_free(&mesh.edge_data, mesh.edges_num);
CustomData_reset(&mesh.edge_data);
mesh.edges_num = new_totedge;
mesh.edges_num = edge_offsets.total_size();
attributes.add<int2>(".edge_verts", AttrDomain::Edge, AttributeInitMoveArray(new_edges.data()));
if (select_new_edges) {
@ -213,14 +225,10 @@ void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, const bool select_new
SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_span<bool>(
".select_edge", AttrDomain::Edge);
if (select_edge) {
int new_edge_index = 0;
for (const calc_edges::EdgeMap &edge_map : edge_maps) {
for (const calc_edges::EdgeMap::Item item : edge_map.items()) {
if (item.value.original_edge == nullptr) {
select_edge.span[new_edge_index] = true;
}
new_edge_index++;
}
select_edge.span.fill(true);
if (!original_edges.is_empty()) {
calc_edges::deselect_known_edges(
edge_offsets, edge_maps, parallel_mask, original_edges, select_edge.span);
}
select_edge.finish();
}
@ -232,7 +240,7 @@ void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, const bool select_new
}
/* Explicitly clear edge maps, because that way it can be parallelized. */
clear_hash_tables(edge_maps);
calc_edges::clear_hash_tables(edge_maps);
}
} // namespace blender::bke

View File

@ -929,27 +929,26 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
return mesh_in_bmain;
}
/* Make sure mesh only points original data-blocks, also increase users of materials and other
* possibly referenced data-blocks.
/* Make sure mesh only points to original data-blocks. Also increase user count of materials and
* other possibly referenced data-blocks.
*
* Going to original data-blocks is required to have bmain in a consistent state, where
* Changing to original data-blocks is required to have bmain in a consistent state, where
* everything is only allowed to reference original data-blocks.
*
* Note that user-count updates has to be done *after* mesh has been transferred to Main database
* (since doing reference-counting on non-Main IDs is forbidden). */
* Note that user-count updates have to be done *after* the mesh has been transferred to Main
* database (since doing reference-counting on non-Main IDs is forbidden). */
BKE_library_foreach_ID_link(
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
/* Append the mesh to 'bmain'.
* We do it a bit longer way since there is no simple and clear way of adding existing data-block
* to the 'bmain'. So we allocate new empty mesh in the 'bmain' (which guarantees all the naming
* and orders and flags) and move the temporary mesh in place there. */
/* Add the mesh to 'bmain'. We do it in a bit longer way since there is no simple and clear way
* of adding existing data-blocks to the 'bmain'. So we create new empty mesh (which guarantees
* all the naming and order and flags) and move the temporary mesh in place there. */
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, mesh->id.name + 2);
/* NOTE: BKE_mesh_nomain_to_mesh() does not copy materials and instead it preserves them in the
/* NOTE: BKE_mesh_nomain_to_mesh() does not copy materials and instead preserves them in the
* destination mesh. So we "steal" materials before calling it.
*
* TODO(sergey): We really better have a function which gets and ID and accepts it for the bmain.
* TODO(sergey): We really ought to have a function which gets an ID and accepts it into #Main.
*/
mesh_in_bmain->mat = mesh->mat;
mesh_in_bmain->totcol = mesh->totcol;

View File

@ -1348,12 +1348,11 @@ OceanCache *BKE_ocean_init_cache(const char *bakepath,
void BKE_ocean_simulate_cache(OceanCache *och, int frame)
{
char filepath[FILE_MAX];
int f = frame;
/* ibufs array is zero based, but filenames are based on frame numbers */
/* still need to clamp frame numbers to valid range of images on disk though */
CLAMP(frame, och->start, och->end);
f = frame - och->start; /* shift to 0 based */
const int f = frame - och->start; /* shift to 0 based */
/* if image is already loaded in mem, return */
if (och->ibufs_disp[f] != nullptr) {

View File

@ -10,6 +10,7 @@
#include <cstring>
#include <optional>
#include "DNA_object_enums.h"
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
@ -81,7 +82,7 @@ using blender::Span;
using blender::Vector;
using blender::bke::AttrDomain;
static void sculpt_attribute_update_refs(Object *ob);
static void sculpt_attribute_update_refs(Object *ob, PBVHType pbvhtype);
static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
AttrDomain domain,
eCustomDataType proptype,
@ -250,6 +251,7 @@ const uchar PAINT_CURSOR_VERTEX_PAINT[3] = {255, 255, 255};
const uchar PAINT_CURSOR_WEIGHT_PAINT[3] = {200, 200, 255};
const uchar PAINT_CURSOR_TEXTURE_PAINT[3] = {255, 255, 255};
const uchar PAINT_CURSOR_SCULPT_CURVES[3] = {255, 100, 100};
const uchar PAINT_CURSOR_SCULPT_GREASE_PENCIL[3] = {255, 100, 100};
static ePaintOverlayControlFlags overlay_flags = (ePaintOverlayControlFlags)0;
@ -361,6 +363,9 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, PaintMode mode)
case PaintMode::SculptCurves:
paint_ptr = (Paint **)&ts->curves_sculpt;
break;
case PaintMode::SculptGreasePencil:
paint_ptr = (Paint **)&ts->gp_sculptpaint;
break;
case PaintMode::Invalid:
break;
}
@ -398,6 +403,8 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode)
return &ts->gp_weightpaint->paint;
case PaintMode::SculptCurves:
return &ts->curves_sculpt->paint;
case PaintMode::SculptGreasePencil:
return &ts->gp_sculptpaint->paint;
case PaintMode::Invalid:
return nullptr;
default:
@ -432,6 +439,8 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode m
return rna_enum_brush_gpencil_weight_types_items;
case PaintMode::SculptCurves:
return rna_enum_brush_curves_sculpt_tool_items;
case PaintMode::SculptGreasePencil:
return rna_enum_brush_gpencil_sculpt_types_items;
case PaintMode::Invalid:
break;
}
@ -462,6 +471,8 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(const PaintMode mode)
return "gpencil_weight_tool";
case PaintMode::SculptCurves:
return "curves_sculpt_tool";
case PaintMode::SculptGreasePencil:
return "gpencil_sculpt_tool";
case PaintMode::Invalid:
break;
}
@ -485,6 +496,7 @@ const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(const Pai
case PaintMode::SculptGPencil:
case PaintMode::WeightGPencil:
case PaintMode::SculptCurves:
case PaintMode::SculptGreasePencil:
case PaintMode::Invalid:
break;
}
@ -596,7 +608,13 @@ PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
case OB_MODE_SCULPT:
return PaintMode::Sculpt;
case OB_MODE_SCULPT_GPENCIL_LEGACY:
return PaintMode::SculptGPencil;
if (obact->type == OB_GPENCIL_LEGACY) {
return PaintMode::SculptGPencil;
}
if (obact->type == OB_GREASE_PENCIL) {
return PaintMode::SculptGreasePencil;
}
return PaintMode::Invalid;
case OB_MODE_WEIGHT_GPENCIL_LEGACY:
return PaintMode::WeightGPencil;
case OB_MODE_VERTEX_PAINT:
@ -648,6 +666,8 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
return PaintMode::SculptCurves;
case CTX_MODE_PAINT_GREASE_PENCIL:
return PaintMode::GPencil;
case CTX_MODE_SCULPT_GREASE_PENCIL:
return PaintMode::SculptGreasePencil;
}
}
else if (tref->space_type == SPACE_IMAGE) {
@ -759,6 +779,8 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode)
return offsetof(Brush, gpencil_weight_tool);
case PaintMode::SculptCurves:
return offsetof(Brush, curves_sculpt_tool);
case PaintMode::SculptGreasePencil:
return offsetof(Brush, gpencil_sculpt_tool);
case PaintMode::Invalid:
break; /* We don't use these yet. */
}
@ -1093,6 +1115,8 @@ eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode)
return OB_MODE_SCULPT_CURVES;
case PaintMode::GPencil:
return OB_MODE_PAINT_GREASE_PENCIL;
case PaintMode::SculptGreasePencil:
return OB_MODE_SCULPT_GPENCIL_LEGACY;
case PaintMode::Invalid:
default:
return OB_MODE_OBJECT;
@ -1754,7 +1778,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
BKE_pbvh_subdiv_cgg_set(ss->pbvh, ss->subdiv_ccg);
sculpt_attribute_update_refs(ob);
sculpt_attribute_update_refs(ob, BKE_pbvh_type(ss->pbvh));
sculpt_update_persistent_base(ob);
if (ob->type == OB_MESH) {
@ -2211,7 +2235,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
BKE_pbvh_pmap_set(pbvh, ob->sculpt->vert_to_face_map);
ob->sculpt->pbvh = pbvh;
sculpt_attribute_update_refs(ob);
sculpt_attribute_update_refs(ob, BKE_pbvh_type(pbvh));
return pbvh;
}
@ -2463,7 +2487,7 @@ static bool sculpt_attribute_create(SculptSession *ss,
return true;
}
static bool sculpt_attr_update(Object *ob, SculptAttribute *attr)
static bool sculpt_attr_update(Object *ob, SculptAttribute *attr, PBVHType pbvh_type)
{
SculptSession *ss = ob->sculpt;
int elem_num = sculpt_attr_elem_count_get(ob, attr->domain);
@ -2476,7 +2500,7 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr)
/* Check if we are a coerced simple array and shouldn't be. */
bad |= attr->simple_array && !attr->params.simple_array &&
!ELEM(BKE_pbvh_type(ss->pbvh), PBVH_GRIDS, PBVH_BMESH);
!ELEM(pbvh_type, PBVH_GRIDS, PBVH_BMESH);
CustomData *cdata = sculpt_get_cdata(ob, attr->domain);
if (cdata && !attr->simple_array) {
@ -2507,7 +2531,7 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr)
attr->name,
attr,
&attr->params,
BKE_pbvh_type(ss->pbvh),
pbvh_type,
attr->data_for_bmesh);
}
@ -2548,19 +2572,17 @@ static SculptAttribute *sculpt_alloc_attr(SculptSession *ss)
return nullptr;
}
SculptAttribute *BKE_sculpt_attribute_get(Object *ob,
AttrDomain domain,
eCustomDataType proptype,
const char *name)
/* The PBVH is NOT guaranteed to exist at the point of this method being called. */
static SculptAttribute *sculpt_attribute_get_ex(
Object *ob, PBVHType pbvhtype, AttrDomain domain, eCustomDataType proptype, const char *name)
{
SculptSession *ss = ob->sculpt;
/* See if attribute is cached in ss->temp_attributes. */
SculptAttribute *attr = sculpt_get_cached_layer(ss, domain, proptype, name);
if (attr) {
if (sculpt_attr_update(ob, attr)) {
sculpt_attribute_update_refs(ob);
if (sculpt_attr_update(ob, attr, pbvhtype)) {
sculpt_attribute_update_refs(ob, pbvhtype);
}
return attr;
@ -2605,6 +2627,17 @@ SculptAttribute *BKE_sculpt_attribute_get(Object *ob,
return nullptr;
}
SculptAttribute *BKE_sculpt_attribute_get(Object *ob,
AttrDomain domain,
eCustomDataType proptype,
const char *name)
{
SculptSession *ss = ob->sculpt;
BLI_assert(ss->pbvh != nullptr);
return sculpt_attribute_get_ex(ob, BKE_pbvh_type(ss->pbvh), domain, proptype, name);
}
static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
AttrDomain domain,
eCustomDataType proptype,
@ -2614,10 +2647,10 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
bool flat_array_for_bmesh)
{
SculptSession *ss = ob->sculpt;
SculptAttribute *attr = BKE_sculpt_attribute_get(ob, domain, proptype, name);
SculptAttribute *attr = sculpt_attribute_get_ex(ob, pbvhtype, domain, proptype, name);
if (attr) {
sculpt_attr_update(ob, attr);
sculpt_attr_update(ob, attr, pbvhtype);
/* Since "stroke_only" is not a CustomData flag we have
* to sync its parameter setting manually. Fixes #104618.
@ -2632,7 +2665,7 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
/* Create attribute. */
sculpt_attribute_create(
ss, ob, domain, proptype, name, attr, params, pbvhtype, flat_array_for_bmesh);
sculpt_attribute_update_refs(ob);
sculpt_attribute_update_refs(ob, pbvhtype);
return attr;
}
@ -2700,7 +2733,7 @@ void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob)
}
}
static void sculpt_attribute_update_refs(Object *ob)
static void sculpt_attribute_update_refs(Object *ob, PBVHType pbvhtype)
{
SculptSession *ss = ob->sculpt;
@ -2710,7 +2743,7 @@ static void sculpt_attribute_update_refs(Object *ob)
SculptAttribute *attr = ss->temp_attributes + j;
if (attr->used) {
sculpt_attr_update(ob, attr);
sculpt_attr_update(ob, attr, pbvhtype);
}
}
@ -2804,7 +2837,7 @@ bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr)
CustomData_free_layer(cdata, attr->proptype, totelem, layer_i);
}
sculpt_attribute_update_refs(ob);
sculpt_attribute_update_refs(ob, BKE_pbvh_type(ss->pbvh));
}
attr->data = nullptr;

View File

@ -28,7 +28,7 @@ class OpenvdbTreeSharingInfo : public ImplicitSharingInfo {
void delete_self_with_data() override
{
MEM_freeN(this);
MEM_delete(this);
}
void delete_data_only() override

View File

@ -408,8 +408,8 @@ template<typename T>
/**
* \brief Create an orthographic projection matrix using OpenGL coordinate convention:
* Maps each axis range to [-1..1] range for all axes except Z.
* The Z axis is collapsed to 0 which eliminates the depth component. So it cannot be used with
* depth testing.
* The Z axis is almost collapsed to 0 which eliminates the depth component.
* So it should not be used with depth testing.
* The resulting matrix can be used with either #project_point or #transform_point.
*/
template<typename T> MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top);
@ -1581,7 +1581,8 @@ MatBase<T, 4, 4> orthographic(T left, T right, T bottom, T top, T near_clip, T f
return mat;
}
template<typename T> MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top)
template<typename T>
MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top, T near_clip)
{
const T x_delta = right - left;
const T y_delta = top - bottom;
@ -1592,8 +1593,13 @@ template<typename T> MatBase<T, 4, 4> orthographic_infinite(T left, T right, T b
mat[3][0] = -(right + left) / x_delta;
mat[1][1] = T(2.0) / y_delta;
mat[3][1] = -(top + bottom) / y_delta;
mat[2][2] = 0.0f;
mat[3][2] = 0.0f;
/* Page 17. Choosing an epsilon for 32 bit floating-point precision. */
constexpr float eps = 2.4e-7f;
/* From "Projection Matrix Tricks" by Eric Lengyel GDC 2007.
* Following same procedure as the reference but for orthographic matrix.
* This avoids degenerate matrix (0 determinant). */
mat[2][2] = -eps;
mat[3][2] = -1.0f - eps * near_clip;
}
return mat;
}
@ -1663,7 +1669,7 @@ template<typename T>
MatBase<T, 4, 4> result = mat;
const bool is_perspective = mat[2][3] == -1.0f;
const bool is_perspective_infinite = mat[2][2] == -1.0f;
if (is_perspective | is_perspective_infinite) {
if (is_perspective || is_perspective_infinite) {
result[2][0] -= mat[0][0] * offset.x / math::length(float3(mat[0][0], mat[1][0], mat[2][0]));
result[2][1] -= mat[1][1] * offset.y / math::length(float3(mat[0][1], mat[1][1], mat[2][1]));
}

View File

@ -179,7 +179,7 @@ std::string BLI_uniquename_cb(blender::FunctionRef<bool(blender::StringRef)> uni
* \param name_offset: Offset of name within block structure
* \param name_maxncpy: Maximum length of name area
*/
void BLI_uniquename(struct ListBase *list,
void BLI_uniquename(const struct ListBase *list,
void *vlink,
const char *defname,
char delim,

View File

@ -32,7 +32,9 @@ namespace blender {
* The simplest possible vector set slot. It stores the index and state in a signed integer. If the
* value is negative, it represents empty or occupied state. Otherwise it represents the index.
*/
template<typename Key> class SimpleVectorSetSlot {
template<typename Key, typename IndexT = int64_t> class SimpleVectorSetSlot {
static_assert(std::is_integral_v<IndexT> && std::is_signed_v<IndexT>);
private:
#define s_is_empty -1
#define s_is_removed -2
@ -40,7 +42,7 @@ template<typename Key> class SimpleVectorSetSlot {
/**
* After the default constructor has run, the slot has to be in the empty state.
*/
int64_t state_ = s_is_empty;
IndexT state_ = s_is_empty;
public:
/**
@ -62,7 +64,7 @@ template<typename Key> class SimpleVectorSetSlot {
/**
* Return the stored index. It is assumed that the slot is occupied.
*/
int64_t index() const
IndexT index() const
{
BLI_assert(this->is_occupied());
return state_;
@ -88,7 +90,7 @@ template<typename Key> class SimpleVectorSetSlot {
* Change the state of this slot from empty/removed to occupied. The hash can be used by other
* slot implementations.
*/
void occupy(int64_t index, uint64_t /*hash*/)
void occupy(IndexT index, uint64_t /*hash*/)
{
BLI_assert(!this->is_occupied());
state_ = index;
@ -98,7 +100,7 @@ template<typename Key> class SimpleVectorSetSlot {
* The key has changed its position in the vector, so the index has to be updated. This method
* can assume that the slot is currently occupied.
*/
void update_index(int64_t index)
void update_index(IndexT index)
{
BLI_assert(this->is_occupied());
state_ = index;
@ -116,7 +118,7 @@ template<typename Key> class SimpleVectorSetSlot {
/**
* Return true if this slot is currently occupied and its corresponding key has the given index.
*/
bool has_index(int64_t index) const
bool has_index(IndexT index) const
{
return state_ == index;
}

View File

@ -1204,8 +1204,8 @@ static int delete_soft(const char *file, const char **error_message)
const char *args[5];
const char *process_failed;
char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
char *xdg_session_desktop = getenv("XDG_SESSION_DESKTOP");
const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
const char *xdg_session_desktop = getenv("XDG_SESSION_DESKTOP");
if ((xdg_current_desktop != nullptr && STREQ(xdg_current_desktop, "KDE")) ||
(xdg_session_desktop != nullptr && STREQ(xdg_session_desktop, "KDE")))

View File

@ -53,11 +53,10 @@ bool BLI_tridiagonal_solve(
size_t bytes = sizeof(double) * (uint)count;
double *c1 = (double *)MEM_mallocN(bytes * 2, "tridiagonal_c1d1");
double *d1 = c1 + count;
if (!c1) {
return false;
}
double *d1 = c1 + count;
int i;
double c_prev, d_prev, x_prev;
@ -120,11 +119,10 @@ bool BLI_tridiagonal_solve_cyclic(
size_t bytes = sizeof(float) * (uint)count;
float *tmp = (float *)MEM_mallocN(bytes * 2, "tridiagonal_ex");
float *b2 = tmp + count;
if (!tmp) {
return false;
}
float *b2 = tmp + count;
/* Prepare the non-cyclic system; relies on tridiagonal_solve ignoring values. */
memcpy(b2, b, bytes);

View File

@ -1397,7 +1397,7 @@ static bool is_pwn(const IMesh &tm, const TriMeshTopology &tmtopo)
* the dummy triangle lies, then finding which cell is between
* the two triangles on either side of the dummy.
*/
static int find_cell_for_point_near_edge(mpq3 p,
static int find_cell_for_point_near_edge(const mpq3 &p,
const Edge &e,
const IMesh &tm,
const TriMeshTopology &tmtopo,
@ -3254,7 +3254,7 @@ static void do_dissolve(FaceMergeState *fms)
* \note it is possible that some of the triangles in \a tris have reversed orientation
* to the rest, so we have to handle the two cases separately.
*/
static Vector<Face *> merge_tris_for_face(Vector<int> tris,
static Vector<Face *> merge_tris_for_face(const Vector<int> &tris,
const IMesh &tm,
const IMesh &imesh_in,
IMeshArena *arena)

View File

@ -468,7 +468,10 @@ std::string BLI_uniquename_cb(blender::FunctionRef<bool(blender::StringRef)> uni
* \param name_offset: should be calculated using `offsetof(structname, membername)`
* macro from `stddef.h`
*/
static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, int name_offset)
static bool uniquename_find_dupe(const ListBase *list,
void *vlink,
const char *name,
int name_offset)
{
for (Link *link = static_cast<Link *>(list->first); link; link = link->next) {
if (link != vlink) {
@ -483,7 +486,7 @@ static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name,
}
struct UniqueNameCheckData {
ListBase *lb;
const ListBase *lb;
void *vlink;
int name_offset;
};
@ -495,7 +498,7 @@ static bool uniquename_unique_check(void *arg, const char *name)
return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offset);
}
void BLI_uniquename(ListBase *list,
void BLI_uniquename(const ListBase *list,
void *vlink,
const char *defname,
char delim,

View File

@ -140,7 +140,7 @@ char *BLI_cpu_brand_string(void)
return brand;
}
#else
// No CPUID on ARM64, so we pull from the registry (on Windows) instead
/* No CPUID on ARM64, so we pull from the registry (on Windows) instead. */
DWORD vendorIdentifierLength = 255;
char vendorIdentifier[255];
if (RegGetValueA(HKEY_LOCAL_MACHINE,

View File

@ -140,6 +140,16 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
FROM_DEFAULT_V4_UCHAR(space_view3d.face_mode_select);
}
if (!USER_VERSION_ATLEAST(402, 13)) {
FROM_DEFAULT_V4_UCHAR(space_text.hilite);
FROM_DEFAULT_V4_UCHAR(space_console.console_cursor);
}
if (!USER_VERSION_ATLEAST(402, 14)) {
BLI_uniquename(
&userdef->themes, btheme, "Theme", '.', offsetof(bTheme, name), sizeof(btheme->name));
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a USER_VERSION_ATLEAST check.

View File

@ -691,13 +691,13 @@ static void SMAABlendingWeightCalculationVS(float2 texcoord,
{
pixcoord = texcoord * float2(size);
// We will use these offsets for the searches later on (see @PSEUDO_GATHER4):
/* We will use these offsets for the searches later on (see @PSEUDO_GATHER4): */
offset[0] = float4(texcoord.xy(), texcoord.xy()) +
float4(-0.25f, -0.125f, 1.25f, -0.125f) / float4(size, size);
offset[1] = float4(texcoord.xy(), texcoord.xy()) +
float4(-0.125f, -0.25f, -0.125f, 1.25f) / float4(size, size);
// And these for the searches, they indicate the ends of the loops:
/* And these for the searches, they indicate the ends of the loops: */
offset[2] = float4(offset[0].x, offset[0].z, offset[1].y, offset[1].w) +
(float4(-2.0f, 2.0f, -2.0f, 2.0f) * float(SMAA_MAX_SEARCH_STEPS)) /
float4(float2(size.x), float2(size.y));
@ -710,7 +710,7 @@ static void SMAANeighborhoodBlendingVS(float2 texcoord, int2 size, float4 &offse
{
offset = float4(texcoord, texcoord) + float4(1.0f, 0.0f, 0.0f, 1.0f) / float4(size, size);
}
#endif // SMAA_INCLUDE_VS
#endif /* SMAA_INCLUDE_VS */
/**
* Luma Edge Detection
@ -732,11 +732,11 @@ static float2 SMAALumaEdgeDetectionPS(float2 texcoord,
float2 threshold = SMAACalculatePredicatedThreshold(
texcoord, offset, SMAATexturePass2D(predicationTex));
#else
// Calculate the threshold:
/* Calculate the threshold: */
float2 threshold = float2(edge_threshold, edge_threshold);
#endif
// Calculate lumas:
/* Calculate lumas: */
// float4 weights = float4(0.2126, 0.7152, 0.0722, 0.0);
float4 weights = float4(luminance_coefficients, 0.0f);
float L = math::dot(SMAASamplePoint(colorTex, texcoord), weights);
@ -744,40 +744,40 @@ static float2 SMAALumaEdgeDetectionPS(float2 texcoord,
float Lleft = math::dot(SMAASamplePoint(colorTex, offset[0].xy()), weights);
float Ltop = math::dot(SMAASamplePoint(colorTex, offset[0].zw()), weights);
// We do the usual threshold:
/* We do the usual threshold: */
float4 delta;
float2 delta_left_top = math::abs(L - float2(Lleft, Ltop));
delta.x = delta_left_top.x;
delta.y = delta_left_top.y;
float2 edges = math::step(threshold, delta.xy());
// Then return early if there is no edge:
/* Then return early if there is no edge: */
if (math::dot(edges, float2(1.0f, 1.0f)) == 0.0f) {
return float2(0.0f);
}
// Calculate right and bottom deltas:
/* Calculate right and bottom deltas: */
float Lright = math::dot(SMAASamplePoint(colorTex, offset[1].xy()), weights);
float Lbottom = math::dot(SMAASamplePoint(colorTex, offset[1].zw()), weights);
float2 delta_right_bottom = math::abs(L - float2(Lright, Lbottom));
delta.z = delta_right_bottom.x;
delta.w = delta_right_bottom.y;
// Calculate the maximum delta in the direct neighborhood:
/* Calculate the maximum delta in the direct neighborhood: */
float2 maxDelta = math::max(delta.xy(), delta.zw());
// Calculate left-left and top-top deltas:
/* Calculate left-left and top-top deltas: */
float Lleftleft = math::dot(SMAASamplePoint(colorTex, offset[2].xy()), weights);
float Ltoptop = math::dot(SMAASamplePoint(colorTex, offset[2].zw()), weights);
float2 delta_left_left_top_top = math::abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop));
delta.z = delta_left_left_top_top.x;
delta.w = delta_left_left_top_top.y;
// Calculate the final maximum delta:
/* Calculate the final maximum delta: */
maxDelta = math::max(maxDelta.xy(), delta.zw());
float finalDelta = math::max(maxDelta.x, maxDelta.y);
// Local contrast adaptation:
/* Local contrast adaptation: */
edges *= math::step(finalDelta, local_contrast_adaptation_factor * delta.xy());
return edges;
@ -793,19 +793,20 @@ static float2 SMAALumaEdgeDetectionPS(float2 texcoord,
*/
static float2 SMAADecodeDiagBilinearAccess(float2 e)
{
// Bilinear access for fetching 'e' have a 0.25 offset, and we are
// interested in the R and G edges:
//
// +---G---+-------+
// | x o R x |
// +-------+-------+
//
// Then, if one of these edge is enabled:
// Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0
// Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0
//
// This function will unpack the values (mad + mul + round):
// wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1
/* Bilinear access for fetching 'e' have a 0.25 offset, and we are
* interested in the R and G edges:
*
* +---G---+-------+
* | x o R x |
* +-------+-------+
*
* Then, if one of these edge is enabled:
* Red: `(0.75 * X + 0.25 * 1) => 0.25 or 1.0`
* Green: `(0.75 * 1 + 0.25 * X) => 0.75 or 1.0`
*
* This function will unpack the values `(mad + mul + round)`:
* wolframalpha.com: `round(x * abs(5 * x - 5 * 0.75))` plot 0 to 1
*/
e.x = e.x * math::abs(5.0f * e.x - 5.0f * 0.75f);
return math::round(e);
}
@ -840,7 +841,7 @@ static float2 SMAASearchDiag2(
SMAATexture2D(edgesTex), float2 texcoord, float2 dir, int2 size, float2 &e)
{
float4 coord = float4(texcoord, -1.0f, 1.0f);
coord.x += 0.25f / size.x; // See @SearchDiag2Optimization
coord.x += 0.25f / size.x; /* See @SearchDiag2Optimization */
float3 t = float3(1.0f / float2(size), 1.0f);
while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && coord.w > 0.9f) {
float3 increment = mad(t, float3(dir, 1.0f), coord.xyz());
@ -848,12 +849,12 @@ static float2 SMAASearchDiag2(
coord.y = increment.y;
coord.z = increment.z;
// @SearchDiag2Optimization
// Fetch both edges at once using bilinear filtering:
/* @SearchDiag2Optimization */
/* Fetch both edges at once using bilinear filtering: */
e = SMAASampleLevelZero(edgesTex, coord.xy()).xy();
e = SMAADecodeDiagBilinearAccess(e);
// Non-optimized version:
/* Non-optimized version: */
// e.g = SMAASampleLevelZero(edgesTex, coord.xy).g;
// e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0), size).r;
@ -871,16 +872,16 @@ static float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float
float2 texcoord = mad(
float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist);
// We do a scale and bias for mapping to texel space:
/* We do a scale and bias for mapping to texel space: */
texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5f * SMAA_AREATEX_PIXEL_SIZE);
// Diagonal areas are on the second half of the texture:
/* Diagonal areas are on the second half of the texture: */
texcoord.x += 0.5f;
// Move to proper place, according to the subpixel offset:
/* Move to proper place, according to the subpixel offset: */
texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset;
// Do it!
/* Do it! */
return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord));
}
@ -896,7 +897,7 @@ static float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex),
{
float2 weights = float2(0.0f, 0.0f);
// Search for the line ends:
/* Search for the line ends: */
float4 d;
float2 end;
if (e.x > 0.0f) {
@ -916,8 +917,8 @@ static float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex),
d.w = positive_diagonal.y;
SMAA_BRANCH
if (d.x + d.y > 2.0f) { // d.x + d.y + 1 > 3
// Fetch the crossing edges:
if (d.x + d.y > 2.0f) { /* `d.x + d.y + 1 > 3`. */
/* Fetch the crossing edges: */
float4 coords = float4(texcoord, texcoord) +
float4(-d.x + 0.25f, d.x, d.y, -d.y - 0.25f) / float4(size, size);
float4 c;
@ -933,7 +934,7 @@ static float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex),
c.w = decoded_access.z;
c.z = decoded_access.w;
// Non-optimized version:
/* Non-optimized version: */
// float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy);
// float4 c;
// c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0), size).g;
@ -941,17 +942,17 @@ static float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex),
// c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0), size).g;
// c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1), size).r;
// Merge crossing edges at each side into a single value:
/* Merge crossing edges at each side into a single value: */
float2 cc = mad(float2(2.0f, 2.0f), float2(c.x, c.z), float2(c.y, c.w));
// Remove the crossing edge if we didn't found the end of the line:
/* Remove the crossing edge if we didn't found the end of the line: */
SMAAMovc(math::step(0.9f, d.zw()), cc, float2(0.0f, 0.0f));
// Fetch the areas for this line:
/* Fetch the areas for this line: */
weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy(), cc, subsampleIndices.z);
}
// Search for the line ends:
/* Search for the line ends: */
float2 negative_diagonal = SMAASearchDiag2(
SMAATexturePass2D(edgesTex), texcoord, float2(-1.0f, -1.0f), size, end);
d.x = negative_diagonal.x;
@ -969,8 +970,8 @@ static float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex),
}
SMAA_BRANCH
if (d.x + d.y > 2.0f) { // d.x + d.y + 1 > 3
// Fetch the crossing edges:
if (d.x + d.y > 2.0f) { /* `d.x + d.y + 1 > 3` */
/* Fetch the crossing edges: */
float4 coords = float4(texcoord, texcoord) + float4(-d.x, -d.x, d.y, d.y) / float4(size, size);
float4 c;
c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy(), int2(-1, 0), size).y;
@ -980,10 +981,10 @@ static float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex),
c.w = left_edge.x;
float2 cc = mad(float2(2.0f, 2.0f), float2(c.x, c.z), float2(c.y, c.w));
// Remove the crossing edge if we didn't found the end of the line:
/* Remove the crossing edge if we didn't found the end of the line: */
SMAAMovc(math::step(0.9f, d.zw()), cc, float2(0.0f, 0.0f));
// Fetch the areas for this line:
/* Fetch the areas for this line: */
float2 area = SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy(), cc, subsampleIndices.w).xy();
weights.x += area.y;
weights.y += area.x;
@ -1004,21 +1005,21 @@ static float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex),
*/
static float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset)
{
// The texture is flipped vertically, with left and right cases taking half
// of the space horizontally:
/* The texture is flipped vertically, with left and right cases taking half
* of the space horizontally: */
float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5f, -1.0f);
float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0f);
// Scale and bias to access texel centers:
/* Scale and bias to access texel centers: */
scale += float2(-1.0f, 1.0f);
bias += float2(0.5f, -0.5f);
// Convert from pixel coordinates to texcoords:
// (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped)
/* Convert from pixel coordinates to texcoords:
* (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped). */
scale *= 1.0f / SMAA_SEARCHTEX_PACKED_SIZE;
bias *= 1.0f / SMAA_SEARCHTEX_PACKED_SIZE;
// Lookup the search texture:
/* Lookup the search texture: */
return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias)));
}
@ -1036,8 +1037,8 @@ static float SMAASearchXLeft(
* which edges are active from the four fetched ones.
*/
float2 e = float2(0.0f, 1.0f);
while (texcoord.x > end && e.y > 0.8281f && // Is there some edge not activated?
e.x == 0.0f) // Or is there a crossing edge that breaks the line?
while (texcoord.x > end && e.y > 0.8281f && /* Is there some edge not activated? */
e.x == 0.0f) /* Or is there a crossing edge that breaks the line? */
{
e = SMAASampleLevelZero(edgesTex, texcoord).xy();
texcoord = texcoord - float2(2.0f, 0.0f) / float2(size);
@ -1047,26 +1048,26 @@ static float SMAASearchXLeft(
-(255.0f / 127.0f), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0f), 3.25f);
return texcoord.x + offset / size.x;
// Non-optimized version:
// We correct the previous (-0.25, -0.125) offset we applied:
/* Non-optimized version:
* We correct the previous (-0.25, -0.125) offset we applied: */
// texcoord.x += 0.25 * SMAA_RT_METRICS.x;
// The searches are bias by 1, so adjust the coords accordingly:
/* The searches are bias by 1, so adjust the coords accordingly: */
// texcoord.x += SMAA_RT_METRICS.x;
// Disambiguate the length added by the last step:
// texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step
/* Disambiguate the length added by the last step: */
// texcoord.x += 2.0 * SMAA_RT_METRICS.x; /* Undo last step. */
// texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) *
// SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); return mad(SMAA_RT_METRICS.x, offset,
// texcoord.x);
// SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0);
// return mad(SMAA_RT_METRICS.x, offset, texcoord.x);
}
static float SMAASearchXRight(
SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end, int2 size)
{
float2 e = float2(0.0f, 1.0f);
while (texcoord.x < end && e.y > 0.8281f && // Is there some edge not activated?
e.x == 0.0f) // Or is there a crossing edge that breaks the line?
while (texcoord.x < end && e.y > 0.8281f && /* Is there some edge not activated? */
e.x == 0.0f) /* Or is there a crossing edge that breaks the line? */
{
e = SMAASampleLevelZero(edgesTex, texcoord).xy();
texcoord = texcoord + float2(2.0f, 0.0f) / float2(size);
@ -1080,8 +1081,8 @@ static float SMAASearchYUp(
SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end, int2 size)
{
float2 e = float2(1.0f, 0.0f);
while (texcoord.y > end && e.x > 0.8281f && // Is there some edge not activated?
e.y == 0.0f) // Or is there a crossing edge that breaks the line?
while (texcoord.y > end && e.x > 0.8281f && /* Is there some edge not activated? */
e.y == 0.0f) /* Or is there a crossing edge that breaks the line? */
{
e = SMAASampleLevelZero(edgesTex, texcoord).xy();
texcoord = texcoord - float2(0.0f, 2.0f) / float2(size);
@ -1097,8 +1098,8 @@ static float SMAASearchYDown(
SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end, int2 size)
{
float2 e = float2(1.0f, 0.0f);
while (texcoord.y < end && e.x > 0.8281f && // Is there some edge not activated?
e.y == 0.0f) // Or is there a crossing edge that breaks the line?
while (texcoord.y < end && e.x > 0.8281f && /* Is there some edge not activated? */
e.y == 0.0f) /* Or is there a crossing edge that breaks the line? */
{
e = SMAASampleLevelZero(edgesTex, texcoord).xy();
texcoord = texcoord + float2(0.0f, 2.0f) / float2(size);
@ -1116,18 +1117,18 @@ static float SMAASearchYDown(
*/
static float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset)
{
// Rounding prevents precision errors of bilinear filtering:
/* Rounding prevents precision errors of bilinear filtering: */
float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE),
math::round(4.0f * float2(e1, e2)),
dist);
// We do a scale and bias for mapping to texel space:
/* We do a scale and bias for mapping to texel space: */
texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5f * SMAA_AREATEX_PIXEL_SIZE);
// Move to proper place, according to the subpixel offset:
/* Move to proper place, according to the subpixel offset: */
texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y);
// Do it!
/* Do it! */
return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord));
}
@ -1145,7 +1146,7 @@ static void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex),
float2 leftRight = math::step(d, float2(d.y, d.x));
float2 rounding = (1.0f - corner_rounding / 100.0f) * leftRight;
rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line.
rounding /= leftRight.x + leftRight.y; /* Reduce blending for pixels in the center of a line. */
float2 factor = float2(1.0f, 1.0f);
factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy(), int2(0, 1), size).x;
@ -1192,16 +1193,17 @@ static float4 SMAABlendingWeightCalculationPS(float2 texcoord,
float4 subsampleIndices,
int2 size,
int corner_rounding)
{ // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES.
{
/* Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. */
float4 weights = float4(0.0f, 0.0f, 0.0f, 0.0f);
float2 e = SMAASamplePoint(edgesTex, texcoord).xy();
SMAA_BRANCH
if (e.y > 0.0f) { // Edge at north
if (e.y > 0.0f) { /* Edge at north. */
#if !defined(SMAA_DISABLE_DIAG_DETECTION)
// Diagonals have both north and west edges, so searching for them in
// one of the boundaries is enough.
/* Diagonals have both north and west edges, so searching for them in
* one of the boundaries is enough. */
float2 diagonal_weights = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex),
SMAATexturePass2D(areaTex),
texcoord,
@ -1212,15 +1214,15 @@ static float4 SMAABlendingWeightCalculationPS(float2 texcoord,
weights.x = diagonal_weights.x;
weights.y = diagonal_weights.y;
// We give priority to diagonals, so if we find a diagonal we skip
// horizontal/vertical processing.
/* We give priority to diagonals, so if we find a diagonal we skip
* horizontal/vertical processing. */
SMAA_BRANCH
if (weights.x == -weights.y) { // weights.x + weights.y == 0.0
if (weights.x == -weights.y) { /* `weights.x + weights.y == 0.0` */
#endif
float2 d;
// Find the distance to the left:
/* Find the distance to the left: */
float3 coords;
coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex),
SMAATexturePass2D(searchTex),
@ -1231,12 +1233,12 @@ static float4 SMAABlendingWeightCalculationPS(float2 texcoord,
offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET)
d.x = coords.x;
// Now fetch the left crossing edges, two at a time using bilinear
// filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to
// discern what value each edge has:
/* Now fetch the left crossing edges, two at a time using bilinear
* filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to
* discern what value each edge has: */
float e1 = SMAASampleLevelZero(edgesTex, coords.xy()).x;
// Find the distance to the right:
/* Find the distance to the right: */
coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex),
SMAATexturePass2D(searchTex),
offset[0].zw(),
@ -1244,25 +1246,23 @@ static float4 SMAABlendingWeightCalculationPS(float2 texcoord,
size);
d.y = coords.z;
// We want the distances to be in pixel units (doing this here allows
// better interleaving of arithmetic and memory accesses):
/* We want the distances to be in pixel units (doing this here allows
* better interleaving of arithmetic and memory accesses): */
d = math::abs(math::round(mad(float2(size.x), d, -float2(pixcoord.x))));
// SMAAArea below needs a sqrt, as the areas texture is compressed
// quadratically:
/* SMAAArea below needs a sqrt, as the areas texture is compressed quadratically: */
float2 sqrt_d = math::sqrt(d);
// Fetch the right crossing edges:
/* Fetch the right crossing edges: */
float e2 =
SMAASampleLevelZeroOffset(edgesTex, float2(coords.z, coords.y), int2(1, 0), size).x;
// Ok, we know how this pattern looks like, now it is time for getting
// the actual area:
/* Ok, we know how this pattern looks like, now it is time for getting the actual area: */
float2 area = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y);
weights.x = area.x;
weights.y = area.y;
// Fix corners:
/* Fix corners: */
coords.y = texcoord.y;
float2 corner_weight = weights.xy();
@ -1278,15 +1278,15 @@ static float4 SMAABlendingWeightCalculationPS(float2 texcoord,
#if !defined(SMAA_DISABLE_DIAG_DETECTION)
}
else
e.x = 0.0f; // Skip vertical processing.
e.x = 0.0f; /* Skip vertical processing. */
#endif
}
SMAA_BRANCH
if (e.x > 0.0f) { // Edge at west
if (e.x > 0.0f) { /* Edge at west. */
float2 d;
// Find the distance to the top:
/* Find the distance to the top: */
float3 coords;
coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex),
SMAATexturePass2D(searchTex),
@ -1296,10 +1296,10 @@ static float4 SMAABlendingWeightCalculationPS(float2 texcoord,
coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x;
d.x = coords.y;
// Fetch the top crossing edges:
/* Fetch the top crossing edges: */
float e1 = SMAASampleLevelZero(edgesTex, coords.xy()).y;
// Find the distance to the bottom:
/* Find the distance to the bottom: */
coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex),
SMAATexturePass2D(searchTex),
offset[1].zw(),
@ -1307,22 +1307,21 @@ static float4 SMAABlendingWeightCalculationPS(float2 texcoord,
size);
d.y = coords.z;
// We want the distances to be in pixel units:
/* We want the distances to be in pixel units: */
d = math::abs(math::round(mad(float2(size.y), d, -float2(pixcoord.y))));
// SMAAArea below needs a sqrt, as the areas texture is compressed
// quadratically:
/* SMAAArea below needs a sqrt, as the areas texture is compressed quadratically: */
float2 sqrt_d = math::sqrt(d);
// Fetch the bottom crossing edges:
/* Fetch the bottom crossing edges: */
float e2 = SMAASampleLevelZeroOffset(edgesTex, float2(coords.x, coords.z), int2(0, 1), size).y;
// Get the area for this direction:
/* Get the area for this direction: */
float2 area = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x);
weights.z = area.x;
weights.w = area.y;
// Fix corners:
/* Fix corners: */
coords.x = texcoord.x;
float2 corner_weight = weights.zw();
@ -1351,14 +1350,14 @@ static float4 SMAANeighborhoodBlendingPS(float2 texcoord,
#endif
int2 size)
{
// Fetch the blending weights for current pixel:
/* Fetch the blending weights for current pixel: */
float4 a;
a.x = SMAASample(blendTex, offset.xy()).w; // Right
a.y = SMAASample(blendTex, offset.zw()).y; // Top
a.z = SMAASample(blendTex, texcoord).z; // Left
a.w = SMAASample(blendTex, texcoord).x; // Bottom
// Is there any blending weight with a value greater than 0.0?
/* Is there any blending weight with a value greater than 0.0? */
SMAA_BRANCH
if (math::dot(a, float4(1.0f, 1.0f, 1.0f, 1.0f)) < 1e-5f) {
float4 color = SMAASampleLevelZero(colorTex, texcoord);
@ -1366,38 +1365,37 @@ static float4 SMAANeighborhoodBlendingPS(float2 texcoord,
#if SMAA_REPROJECTION
float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord));
// Pack velocity into the alpha channel:
/* Pack velocity into the alpha channel: */
color.a = math::sqrt(5.0f * math::length(velocity));
#endif
return color;
}
else {
bool h = math::max(a.x, a.z) > math::max(a.y, a.w); // max(horizontal) > max(vertical)
bool h = math::max(a.x, a.z) > math::max(a.y, a.w); /* `max(horizontal) > max(vertical)`. */
// Calculate the blending offsets:
/* Calculate the blending offsets: */
float4 blendingOffset = float4(0.0f, a.y, 0.0f, a.w);
float2 blendingWeight = float2(a.y, a.w);
SMAAMovc(float4(h), blendingOffset, float4(a.x, 0.0f, a.z, 0.0f));
SMAAMovc(float2(h), blendingWeight, float2(a.x, a.z));
blendingWeight /= math::dot(blendingWeight, float2(1.0f, 1.0f));
// Calculate the texture coordinates:
/* Calculate the texture coordinates: */
float4 blendingCoord = float4(texcoord, texcoord) + blendingOffset / float4(size, -size);
// We exploit bilinear filtering to mix current pixel with the chosen
// neighbor:
/* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */
float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy());
color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw());
#if SMAA_REPROJECTION
// Antialias velocity for proper reprojection in a later stage:
/* Antialias velocity for proper reprojection in a later stage: */
float2 velocity = blendingWeight.x *
SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy()));
velocity += blendingWeight.y *
SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw()));
// Pack velocity into the alpha channel:
/* Pack velocity into the alpha channel: */
color.a = math::sqrt(5.0f * math::length(velocity));
#endif

View File

@ -198,6 +198,7 @@ set(GLSL_SRC
shaders/compositor_projector_lens_distortion.glsl
shaders/compositor_read_input.glsl
shaders/compositor_realize_on_domain.glsl
shaders/compositor_scale_variable.glsl
shaders/compositor_screen_lens_distortion.glsl
shaders/compositor_smaa_blending_weight_calculation.glsl
shaders/compositor_smaa_edge_detection.glsl
@ -321,6 +322,7 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/compositor_projector_lens_distortion_info.hh
shaders/infos/compositor_read_input_info.hh
shaders/infos/compositor_realize_on_domain_info.hh
shaders/infos/compositor_scale_variable_info.hh
shaders/infos/compositor_screen_lens_distortion_info.hh
shaders/infos/compositor_smaa_info.hh
shaders/infos/compositor_split_info.hh

View File

@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
ivec2 input_size = texture_size(input_tx);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(input_size);
vec2 center = vec2(0.5);
vec2 scale = vec2(texture_load(x_scale_tx, texel).x, texture_load(y_scale_tx, texel).x);
vec2 scaled_coordinates = center + (coordinates - center) / max(scale, 0.0001);
imageStore(output_img, texel, texture(input_tx, scaled_coordinates));
}

View File

@ -0,0 +1,14 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_scale_variable)
.local_group_size(16, 16)
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.sampler(1, ImageType::FLOAT_2D, "x_scale_tx")
.sampler(2, ImageType::FLOAT_2D, "y_scale_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_scale_variable.glsl")
.do_static_compilation(true);

View File

@ -406,6 +406,12 @@ void DepsgraphNodeBuilder::begin_build()
saved_entry_tags_.append_as(op_node);
}
for (const OperationNode *op_node : graph_->operations) {
if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
needs_update_operations_.append_as(op_node);
}
}
/* Make sure graph has no nodes left from previous state. */
graph_->clear_all_nodes();
graph_->operations.clear();
@ -537,6 +543,16 @@ void DepsgraphNodeBuilder::tag_previously_tagged_nodes()
* that originally node was explicitly tagged for user update. */
operation_node->tag_update(graph_, DEG_UPDATE_SOURCE_USER_EDIT);
}
/* Restore needs-update flags since the previous state of the dependency graph, ensuring the
* previously-skipped operations are properly re-evaluated when needed. */
for (const OperationKey &operation_key : needs_update_operations_) {
OperationNode *operation_node = find_operation_node(operation_key);
if (operation_node == nullptr) {
continue;
}
operation_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
}
}
void DepsgraphNodeBuilder::end_build()

View File

@ -291,9 +291,14 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
};
protected:
/* Entry tags from the previous state of the dependency graph.
/* Entry tags and non-updated operations from the previous state of the dependency graph.
* The entry tags are operations which were directly tagged, the matching operations from the
* new dependency graph will be tagged. The needs-update operations are possibly indirectly
* modified operations, whose complementary part from the new dependency graph will only be
* marked as needs-update.
* Stored before the graph is re-created so that they can be transferred over. */
Vector<PersistentOperationKey> saved_entry_tags_;
Vector<PersistentOperationKey> needs_update_operations_;
struct BuilderWalkUserData {
DepsgraphNodeBuilder *builder;

View File

@ -458,6 +458,9 @@ void Film::sync()
accumulate_ps_.specialize_constant(sh, "samples_len", &data_.samples_len);
accumulate_ps_.specialize_constant(sh, "use_reprojection", &use_reprojection_);
accumulate_ps_.specialize_constant(sh, "scaling_factor", data_.scaling_factor);
accumulate_ps_.specialize_constant(sh, "combined_id", &data_.combined_id);
accumulate_ps_.specialize_constant(sh, "display_id", &data_.display_id);
accumulate_ps_.specialize_constant(sh, "normal_id", &data_.normal_id);
accumulate_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS);
accumulate_ps_.shader_set(sh);
accumulate_ps_.bind_resources(inst_.uniform_data);

View File

@ -278,8 +278,8 @@ class DeferredLayer : DeferredLayerBase {
void render(View &main_view,
View &render_view,
Framebuffer &prepass_fb,
Framebuffer &gbuffer_fb,
Framebuffer &combined_fb,
Framebuffer &gbuffer_fb,
int2 extent,
RayTraceBuffer &rt_buffer,
bool is_first_pass);

View File

@ -506,8 +506,13 @@ struct VolumesInfoData {
* are not invertible. We store the finite projection matrix and use it for this purpose. */
float4x4 winmat_finite;
float4x4 wininv_finite;
/* Convert volume frustum UV(+ linear Z) coordinates into previous frame UV(+ linear Z). */
float4x4 history_matrix;
/* Copies of the matrices above but without jittering. Used for re-projection. */
float4x4 wininv_stable;
float4x4 winmat_stable;
/* Previous render sample copy of winmat_stable. */
float4x4 history_winmat_stable;
/* Transform from current view space to previous render sample view space. */
float4x4 curr_view_to_past_view;
/* Size of the froxel grid texture. */
packed_int3 tex_size;
/* Maximum light intensity during volume lighting evaluation. */
@ -529,7 +534,14 @@ struct VolumesInfoData {
float depth_near;
float depth_far;
float depth_distribution;
float _pad0;
/* Previous render sample copy of the depth mapping parameters. */
float history_depth_near;
float history_depth_far;
float history_depth_distribution;
/* Amount of history to blend during the scatter phase. */
float history_opacity;
float _pad1;
};
BLI_STATIC_ASSERT_ALIGN(VolumesInfoData, 16)

View File

@ -126,7 +126,7 @@ void ShadingView::render()
inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
inst_.volume.draw_prepass(render_view_);
inst_.volume.draw_prepass(main_view_);
/* TODO(Miguel Pozo): Deferred and forward prepass should happen before the GBuffer pass. */
inst_.pipelines.deferred.render(main_view_,
@ -140,7 +140,7 @@ void ShadingView::render()
inst_.gbuffer.release();
inst_.volume.draw_compute(render_view_);
inst_.volume.draw_compute(main_view_);
// inst_.lookdev.render_overlay(view_fb_);

View File

@ -19,7 +19,6 @@
#include "eevee_pipeline.hh"
#include "eevee_volume.hh"
#include <iostream>
namespace blender::eevee {
@ -47,21 +46,16 @@ void VolumeModule::init()
data_.coord_scale = float2(extent) / float2(tile_size * tex_size);
data_.main_view_extent = float2(extent);
data_.main_view_extent_inv = 1.0f / float2(extent);
data_.tex_size = tex_size;
data_.inv_tex_size = 1.0f / float3(tex_size);
/* TODO: compute snap to maxZBuffer for clustered rendering. */
if (data_.tex_size != tex_size) {
data_.tex_size = tex_size;
data_.inv_tex_size = 1.0f / float3(tex_size);
}
if ((scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_SHADOWS) == 0) {
data_.shadow_steps = 0;
}
else {
data_.shadow_steps = float(scene_eval->eevee.volumetric_shadow_samples);
}
const bool shadow_enabled = (scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_SHADOWS) != 0;
data_.shadow_steps = (shadow_enabled) ? scene_eval->eevee.volumetric_shadow_samples : 0;
data_.light_clamp = scene_eval->eevee.volumetric_light_clamp;
data_.light_clamp = (data_.light_clamp > 0.0) ? data_.light_clamp : 1e20;
use_reprojection_ = (scene_eval->eevee.flag & SCE_EEVEE_TAA_REPROJECTION) != 0;
}
void VolumeModule::begin_sync() {}
@ -83,7 +77,6 @@ void VolumeModule::end_sync()
}
std::optional<Bounds<float>> volume_bounds = inst_.pipelines.volume.object_integration_range();
if (volume_bounds && !inst_.world.has_volume()) {
/* Restrict integration range to the object volume range. This increases precision. */
integration_start = math::max(integration_start, -volume_bounds.value().max);
@ -93,6 +86,13 @@ void VolumeModule::end_sync()
float near = math::min(-integration_start, clip_start + 1e-4f);
float far = math::max(-integration_end, clip_end - 1e-4f);
if (assign_if_different(history_camera_is_perspective_, inst_.camera.is_perspective())) {
/* Currently, the re-projection uses the same path for volume_z_to_view_z conversion for both
* the current view and the history view. Moreover, re-projecting in this huge change is more
* detrimental than anything. */
valid_history_ = false;
}
if (inst_.camera.is_perspective()) {
float sample_distribution = scene_eval->eevee.volumetric_sample_distribution;
sample_distribution = 4.0f * math::max(1.0f - sample_distribution, 1e-2f);
@ -132,6 +132,9 @@ void VolumeModule::end_sync()
occupancy.occupancy_tx_ = nullptr;
occupancy.hit_depth_tx_ = nullptr;
occupancy.hit_count_tx_ = nullptr;
/* Avoid undefined re-projection behavior. */
valid_history_ = false;
return;
}
@ -177,12 +180,15 @@ void VolumeModule::end_sync()
front_depth_tx_.ensure_2d(GPU_DEPTH24_STENCIL8, data_.tex_size.xy(), front_depth_usage);
occupancy_fb_.ensure(GPU_ATTACHMENT_TEXTURE(front_depth_tx_));
scatter_tx_.current().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
extinction_tx_.current().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
scatter_tx_.previous().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
extinction_tx_.previous().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
bool created = false;
created |= scatter_tx_.current().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
created |= extinction_tx_.current().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
created |= scatter_tx_.previous().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
created |= extinction_tx_.previous().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
data_.history_matrix = float4x4::identity();
if (created) {
valid_history_ = false;
}
integrated_scatter_tx_.ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
integrated_transmit_tx_.ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
@ -199,12 +205,12 @@ void VolumeModule::end_sync()
occupancy.hit_depth_tx_ = hit_depth_tx_;
occupancy.hit_count_tx_ = hit_count_tx_;
/* Use custom sampler to simplify and speedup the shader.
* - Set extend mode to clamp to border color to reject samples with invalid re-projection.
* - Set filtering mode to none to avoid over-blur during re-projection. */
const GPUSamplerState history_sampler = {GPU_SAMPLER_FILTERING_DEFAULT,
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER,
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER};
/* Set extend mode to extend and reject invalid samples in the shader.
* This avoids some black rim artifacts near the edge of the re-projected volume.
* Filter linear to avoid sharp artifacts during re-projection. */
const GPUSamplerState history_sampler = {GPU_SAMPLER_FILTERING_LINEAR,
GPU_SAMPLER_EXTEND_MODE_EXTEND,
GPU_SAMPLER_EXTEND_MODE_EXTEND};
scatter_ps_.init();
scatter_ps_.shader_set(
inst_.shaders.static_shader_get(use_lights_ ? VOLUME_SCATTER_WITH_LIGHTS : VOLUME_SCATTER));
@ -253,18 +259,65 @@ void VolumeModule::end_sync()
resolve_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
void VolumeModule::draw_prepass(View &view)
void VolumeModule::draw_prepass(View &main_view)
{
if (!enabled_) {
return;
}
DRW_stats_group_start("Volumes");
inst_.pipelines.world_volume.render(view);
/* Number of frame to consider for blending with exponential (infinite) average. */
int exponential_frame_count = 16;
if (inst_.is_image_render()) {
/* Disable reprojection for rendering. */
exponential_frame_count = 0;
}
else if (!use_reprojection_) {
/* No re-projection if TAA is disabled. */
exponential_frame_count = 0;
}
else if (inst_.is_playback()) {
/* For now, we assume we want responsiveness for volume animation.
* But this makes general animation inside uniform volumes less stable.
* When we introduce updated volume tagging, we will be able to increase this for general
* playback. */
exponential_frame_count = 3;
}
else if (inst_.is_transforming()) {
/* Improve responsiveness of volume if we are transforming objects. */
/* TODO(fclem): This is too general as it will be triggered even for non volume object.
* Instead, we should tag which areas of the volume that need increased responsiveness. */
exponential_frame_count = 3;
}
else if (inst_.is_navigating()) {
/* Navigation is usually smooth because of the re-projection but we can get ghosting
* artifacts on lights because of voxels stretched in Z or anisotropy. */
exponential_frame_count = 8;
}
else if (inst_.sampling.is_reset()) {
/* If we are not falling in any cases above, this usually means there is a scene or object
* parameter update. Reset accumulation completely. */
exponential_frame_count = 0;
}
if (!valid_history_) {
history_frame_count_ = 0;
}
/* Interactive mode accumulate samples using exponential average.
* We still reuse the history when we go into static mode.
* However, using re-projection for static mode will show the precision limit of RG11B10 format.
* So we clamp it to the exponential frame count in any case. */
history_frame_count_ = math::min(history_frame_count_, exponential_frame_count);
/* In interactive mode, use exponential average (fixed ratio).
* For static / render mode use simple average (moving ratio). */
float history_opacity = history_frame_count_ / (history_frame_count_ + 1.0f);
/* Setting opacity to 0.0 will bypass any sampling of history buffer.
* Allowing us to skip the 3D texture clear. */
data_.history_opacity = (valid_history_) ? history_opacity : 0.0f;
float left, right, bottom, top, near, far;
const float4x4 winmat_view = view.winmat();
projmat_dimensions(winmat_view.ptr(), &left, &right, &bottom, &top, &near, &far);
projmat_dimensions(main_view.winmat().ptr(), &left, &right, &bottom, &top, &near, &far);
/* Just like up-sampling matrix computation, we have to be careful to where to put the bounds of
* our froxel volume so that a 2D pixel covers exactly the number of pixel in a tile. */
@ -275,18 +328,20 @@ void VolumeModule::draw_prepass(View &view)
right = left + volume_size.x;
top = bottom + volume_size.y;
/* TODO(fclem): These new matrices are created from the jittered main view matrix. It should be
* better to create them from the non-jittered one to avoid over-blurring. */
float4x4 winmat_infinite, winmat_finite;
/* Create an infinite projection matrix to avoid far clipping plane clipping the object. This
* way, surfaces that are further away than the far clip plane will still be voxelized.*/
winmat_infinite = view.is_persp() ?
winmat_infinite = main_view.is_persp() ?
math::projection::perspective_infinite(left, right, bottom, top, near) :
math::projection::orthographic_infinite(left, right, bottom, top);
math::projection::orthographic_infinite(left, right, bottom, top, near);
/* We still need a bounded projection matrix to get correct froxel location. */
winmat_finite = view.is_persp() ?
winmat_finite = main_view.is_persp() ?
math::projection::perspective(left, right, bottom, top, near, far) :
math::projection::orthographic(left, right, bottom, top, near, far);
/* Save non-jittered finite matrix for re-projection. */
data_.winmat_stable = winmat_finite;
data_.wininv_stable = math::invert(winmat_finite);
/* Anti-Aliasing / Super-Sampling jitter. */
float2 jitter = inst_.sampling.rng_2d_get(SAMPLING_VOLUME_U);
/* Wrap to keep first sample centered (0,0) and scale to convert to NDC. */
@ -299,9 +354,16 @@ void VolumeModule::draw_prepass(View &view)
data_.winmat_finite = winmat_finite;
data_.wininv_finite = math::invert(winmat_finite);
/* Compute re-projection matrix. */
data_.curr_view_to_past_view = history_viewmat_ * main_view.viewinv();
inst_.uniform_data.push_update();
volume_view.sync(view.viewmat(), winmat_infinite);
DRW_stats_group_start("Volumes");
inst_.pipelines.world_volume.render(main_view);
volume_view.sync(main_view.viewmat(), winmat_infinite);
if (inst_.pipelines.volume.is_enabled()) {
occupancy_fb_.bind();
@ -310,7 +372,7 @@ void VolumeModule::draw_prepass(View &view)
DRW_stats_group_end();
}
void VolumeModule::draw_compute(View &view)
void VolumeModule::draw_compute(View &main_view)
{
if (!enabled_) {
return;
@ -318,8 +380,17 @@ void VolumeModule::draw_compute(View &view)
scatter_tx_.swap();
extinction_tx_.swap();
inst_.manager->submit(scatter_ps_, view);
inst_.manager->submit(integration_ps_, view);
inst_.manager->submit(scatter_ps_, main_view);
inst_.manager->submit(integration_ps_, main_view);
/* Copy history data. */
history_viewmat_ = main_view.viewmat();
data_.history_depth_near = data_.depth_near;
data_.history_depth_far = data_.depth_far;
data_.history_depth_distribution = data_.depth_distribution;
data_.history_winmat_stable = data_.winmat_stable;
valid_history_ = true;
history_frame_count_ += 1;
}
void VolumeModule::draw_resolve(View &view)

View File

@ -51,6 +51,7 @@ class VolumeModule {
Instance &inst_;
bool enabled_;
bool use_reprojection_;
bool use_lights_;
VolumesInfoData &data_;
@ -96,6 +97,15 @@ class VolumeModule {
View volume_view = {"Volume View"};
float4x4 history_viewmat_ = float4x4::zero();
/* Number of re-projected frame into the volume history.
* Allows continuous integration between interactive and static mode. */
int history_frame_count_ = 0;
/* Used to detect change in camera projection type. */
bool history_camera_is_perspective_ = false;
/* Must be set to false on every event that makes the history invalid to sample. */
bool valid_history_ = false;
public:
VolumeModule(Instance &inst, VolumesInfoData &data) : inst_(inst), data_(data)
{
@ -127,9 +137,9 @@ class VolumeModule {
void end_sync();
/* Render material properties. */
void draw_prepass(View &view);
void draw_prepass(View &main_view);
/* Compute scattering and integration. */
void draw_compute(View &view);
void draw_compute(View &main_view);
/* Final image compositing. */
void draw_resolve(View &view);

View File

@ -2,6 +2,9 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
/* -------------------------------------------------------------------- */
/** \name YCoCg
* \{ */
@ -55,15 +58,11 @@ vec3 colorspace_safe_color(vec3 c)
/**
* Clamp all components to the specified maximum and avoid color shifting.
*/
vec3 colorspace_brightness_clamp_max(vec3 color, float max_value)
vec3 colorspace_brightness_clamp_max(vec3 color, float limit)
{
float luma = max(1e-8, max(max(color.r, color.g), color.b));
if (luma < 1e-8) {
return color;
}
return color * (1.0 - max(0.0, luma - max_value) / luma);
return color * saturate(limit / max(1e-8, reduce_max(abs(color))));
}
vec4 colorspace_brightness_clamp_max(vec4 color, float max_value)
vec4 colorspace_brightness_clamp_max(vec4 color, float limit)
{
return vec4(colorspace_brightness_clamp_max(color.rgb, max_value), color.a);
return vec4(colorspace_brightness_clamp_max(color.rgb, limit), color.a);
}

View File

@ -12,20 +12,19 @@ void main()
if (uniform_buf.film.display_only) {
out_depth = imageLoad(depth_img, texel_film).r;
if (uniform_buf.film.display_id == -1) {
if (display_id == -1) {
out_color = texelFetch(in_combined_tx, texel_film, 0);
}
else if (uniform_buf.film.display_storage_type == PASS_STORAGE_VALUE) {
out_color.rgb =
imageLoad(value_accum_img, ivec3(texel_film, uniform_buf.film.display_id)).rrr;
out_color.rgb = imageLoad(value_accum_img, ivec3(texel_film, display_id)).rrr;
out_color.a = 1.0;
}
else if (uniform_buf.film.display_storage_type == PASS_STORAGE_COLOR) {
out_color = imageLoad(color_accum_img, ivec3(texel_film, uniform_buf.film.display_id));
out_color = imageLoad(color_accum_img, ivec3(texel_film, display_id));
}
else /* PASS_STORAGE_CRYPTOMATTE */ {
out_color = cryptomatte_false_color(
imageLoad(cryptomatte_img, ivec3(texel_film, uniform_buf.film.display_id)).r);
imageLoad(cryptomatte_img, ivec3(texel_film, display_id)).r);
}
}
else {

View File

@ -87,7 +87,7 @@ float film_weight_accumulation(ivec2 texel_film)
/* TODO(fclem): Reference implementation, also needed for panoramic cameras. */
if (scaling_factor > 1) {
float weight = 0.0;
for (int i = 0; i < uniform_buf.film.samples_len; i++) {
for (int i = 0; i < samples_len; i++) {
weight += film_sample_get(i, texel_film).weight;
}
return weight;
@ -132,7 +132,7 @@ void film_sample_accum_mist(FilmSample samp, inout float accum)
void film_sample_accum_combined(FilmSample samp, inout vec4 accum, inout float weight_accum)
{
if (uniform_buf.film.combined_id == -1) {
if (combined_id == -1) {
return;
}
vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, samp.texel);
@ -436,7 +436,7 @@ float film_history_blend_factor(float velocity,
void film_store_combined(
FilmSample dst, ivec2 src_texel, vec4 color, float color_weight, inout vec4 display)
{
if (uniform_buf.film.combined_id == -1) {
if (combined_id == -1) {
return;
}
@ -499,7 +499,7 @@ void film_store_combined(
color = vec4(0.0, 0.0, 0.0, 1.0);
}
if (uniform_buf.film.display_id == -1) {
if (display_id == -1) {
display = color;
}
imageStore(out_combined_img, dst.texel, color);
@ -520,7 +520,7 @@ void film_store_color(FilmSample dst, int pass_id, vec4 color, inout vec4 displa
color = vec4(0.0, 0.0, 0.0, 1.0);
}
if (uniform_buf.film.display_id == pass_id) {
if (display_id == pass_id) {
display = color;
}
imageStore(color_accum_img, ivec3(dst.texel, pass_id), color);
@ -541,7 +541,7 @@ void film_store_value(FilmSample dst, int pass_id, float value, inout vec4 displ
value = 0.0;
}
if (uniform_buf.film.display_id == pass_id) {
if (display_id == pass_id) {
display = vec4(value, value, value, 1.0);
}
imageStore(value_accum_img, ivec3(dst.texel, pass_id), vec4(value));
@ -554,7 +554,7 @@ void film_store_data(ivec2 texel_film, int pass_id, vec4 data_sample, inout vec4
return;
}
if (uniform_buf.film.display_id == pass_id) {
if (display_id == pass_id) {
display = data_sample;
}
imageStore(color_accum_img, ivec3(texel_film, pass_id), data_sample);
@ -616,7 +616,7 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
/* NOTE: We split the accumulations into separate loops to avoid using too much registers and
* maximize occupancy. */
if (uniform_buf.film.combined_id != -1) {
if (combined_id != -1) {
/* NOTE: Do weight accumulation again since we use custom weights. */
float weight_accum = 0.0;
vec4 combined_accum = vec4(0.0);
@ -643,10 +643,10 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
vector *= vec4(vec2(uniform_buf.film.render_extent), vec2(uniform_buf.film.render_extent));
film_store_depth(texel_film, depth, out_depth);
if (uniform_buf.film.normal_id != -1) {
if (normal_id != -1) {
vec4 normal = texelFetch(
rp_color_tx, ivec3(film_sample.texel, uniform_buf.render_pass.normal_id), 0);
film_store_data(texel_film, uniform_buf.film.normal_id, normal, out_color);
film_store_data(texel_film, normal_id, normal, out_color);
}
if (uniform_buf.film.position_id != -1) {
vec4 position = texelFetch(
@ -658,10 +658,8 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
}
else {
out_depth = imageLoad(depth_img, texel_film).r;
if (uniform_buf.film.display_id != -1 &&
uniform_buf.film.display_id == uniform_buf.film.normal_id)
{
out_color = imageLoad(color_accum_img, ivec3(texel_film, uniform_buf.film.display_id));
if (display_id != -1 && display_id == normal_id) {
out_color = imageLoad(color_accum_img, ivec3(texel_film, display_id));
}
}
}

View File

@ -64,10 +64,12 @@ void main()
}
}
else {
uint hit_id = imageAtomicAdd(hit_count_img, texel, 1u);
if (hit_id < VOLUME_HIT_DEPTH_MAX) {
float value = gl_FrontFacing ? volume_z : -volume_z;
imageStore(hit_depth_img, ivec3(texel, hit_id), vec4(value));
if (volume_z > 0.0) {
uint hit_id = imageAtomicAdd(hit_count_img, texel, 1u);
if (hit_id < VOLUME_HIT_DEPTH_MAX) {
float value = gl_FrontFacing ? volume_z : -volume_z;
imageStore(hit_depth_img, ivec3(texel, hit_id), vec4(value));
}
}
}
}

View File

@ -42,11 +42,8 @@ float volume_z_to_view_z(float z)
/* View space Z to volume froxel texture normalized linear Z.
* Not dependent on projection matrix (as long as drw_view_is_perspective is consistent). */
float view_z_to_volume_z(float depth)
float view_z_to_volume_z(float depth, float near, float far, float distribution)
{
float near = uniform_buf.volumes.depth_near;
float far = uniform_buf.volumes.depth_far;
float distribution = uniform_buf.volumes.depth_distribution;
if (drw_view_is_perspective()) {
/* Exponential distribution. */
return distribution * log2(depth * far + near);
@ -56,6 +53,13 @@ float view_z_to_volume_z(float depth)
return (depth - near) / (far - near);
}
}
float view_z_to_volume_z(float depth)
{
return view_z_to_volume_z(depth,
uniform_buf.volumes.depth_near,
uniform_buf.volumes.depth_far,
uniform_buf.volumes.depth_distribution);
}
/* Jittered volume texture normalized coordinates to view space position. */
vec3 volume_jitter_to_view(vec3 coord)
@ -107,17 +111,42 @@ vec3 volume_screen_to_resolve(vec3 coord)
}
/* Returns the uvw (normalized coordinate) of a froxel in the previous frame.
* If no history exists, it will return out of bounds sampling coordinates. */
vec3 volume_history_position_get(ivec3 froxel)
* Returns vec3(-1) if history is unavailable. */
vec3 volume_history_uvw_get(ivec3 froxel)
{
mat4x4 wininv = uniform_buf.volumes.wininv_stable;
mat4x4 winmat = uniform_buf.volumes.winmat_stable;
/* We can't reproject by a simple matrix multiplication. We first need to remap to the view Z,
* then transform, then remap back to Volume range. */
vec3 uvw = (vec3(froxel) + 0.5) * uniform_buf.volumes.inv_tex_size;
uvw.z = volume_z_to_view_z(uvw.z);
vec3 ndc_P = drw_screen_to_ndc(uvw);
/* We need to recover the NDC position for correct perspective divide. */
float view_z = volume_z_to_view_z(uvw.z);
ndc_P.z = drw_perspective_divide(winmat * vec4(0.0, 0.0, view_z, 1.0)).z;
/* NDC to view. */
vec3 vs_P = project_point(wininv, ndc_P);
vec3 uvw_history = transform_point(uniform_buf.volumes.history_matrix, uvw);
/* TODO(fclem): For now assume same distribution settings. */
uvw_history.z = view_z_to_volume_z(uvw_history.z);
/* Transform to previous camera view space. */
vec3 vs_P_history = transform_point(uniform_buf.volumes.curr_view_to_past_view, vs_P);
/* View to NDC. */
vec4 hs_P_history = uniform_buf.volumes.history_winmat_stable * vec4(vs_P_history, 1.0);
vec3 ndc_P_history = drw_perspective_divide(hs_P_history);
if (hs_P_history.w < 0.0 || any(greaterThan(abs(ndc_P_history.xy), vec2(1.0)))) {
return vec3(-1.0);
}
vec3 uvw_history;
uvw_history.xy = drw_ndc_to_screen(ndc_P_history.xy);
uvw_history.z = view_z_to_volume_z(vs_P_history.z,
uniform_buf.volumes.history_depth_near,
uniform_buf.volumes.history_depth_far,
uniform_buf.volumes.history_depth_distribution);
if (uvw_history.z < 0.0 || uvw_history.z > 1.0) {
return vec3(-1.0);
}
return uvw_history;
}
@ -156,20 +185,6 @@ vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
float power = 1.0;
if (!is_directional) {
float volume_radius_squared = light_local_data_get(light).radius_squared;
float light_clamp = uniform_buf.volumes.light_clamp;
if (light_clamp != 0.0) {
/* 0.0 light clamp means it's disabled. */
float max_power = reduce_max(light.color) * light.power[LIGHT_VOLUME];
if (max_power > 0.0) {
/* The limit of the power attenuation function when the distance to the light goes to 0 is
* `2 / r^2` where r is the light radius. We need to find the right radius that emits at
* most the volume light upper bound. Inverting the function we get: */
float min_radius_squared = 1.0 / (0.5 * light_clamp / max_power);
/* Square it here to avoid a multiplication inside the shader. */
volume_radius_squared = max(volume_radius_squared, min_radius_squared);
}
}
/**
* Using "Point Light Attenuation Without Singularity" from Cem Yuksel
* http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf

View File

@ -14,6 +14,7 @@
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
@ -25,6 +26,7 @@ vec3 volume_scatter_light_eval(
{
LightData light = light_buf[l_idx];
/* TODO(fclem): Own light list for volume without lights that have 0 volume influence. */
if (light.power[LIGHT_VOLUME] == 0.0) {
return vec3(0);
}
@ -41,14 +43,15 @@ vec3 volume_scatter_light_eval(
visibility *= shadow_sample(is_directional, shadow_atlas_tx, shadow_tilemaps_tx, light, P)
.light_visibilty;
}
visibility *= volume_phase_function(-V, lv.L, s_anisotropy);
if (visibility < LIGHT_ATTENUATION_THRESHOLD) {
return vec3(0);
}
vec3 Li = volume_light(light, is_directional, lv) *
vec3 Li = volume_light(light, is_directional, lv) * visibility *
volume_shadow(light, is_directional, P, lv, extinction_tx);
return Li * visibility * volume_phase_function(-V, lv.L, s_anisotropy);
return colorspace_brightness_clamp_max(Li, uniform_buf.volumes.light_clamp);
}
#endif
@ -99,17 +102,16 @@ void main()
scattering += light_scattering * s_scattering;
#endif
#if 0 /* TODO */
{
if (uniform_buf.volumes.history_opacity > 0.0) {
/* Temporal reprojection. */
vec3 uvw_history = volume_history_position_get(froxel);
vec4 scattering_history = texture(scattering_history_tx, uvw_history);
vec4 extinction_history = texture(extinction_history_tx, uvw_history);
float history_opacity = 0.95 * scattering_history.a;
scattering = mix(scattering, scattering_history.rgb, history_opacity);
extinction = mix(extinction, extinction_history.rgb, history_opacity);
vec3 uvw_history = volume_history_uvw_get(froxel);
if (uvw_history.x != -1.0) {
vec3 scattering_history = texture(scattering_history_tx, uvw_history).rgb;
vec3 extinction_history = texture(extinction_history_tx, uvw_history).rgb;
scattering = mix(scattering, scattering_history, uniform_buf.volumes.history_opacity);
extinction = mix(extinction, extinction_history, uniform_buf.volumes.history_opacity);
}
}
#endif
/* Catch NaNs. */
if (any(isnan(scattering)) || any(isnan(extinction))) {

View File

@ -27,6 +27,9 @@ GPU_SHADER_CREATE_INFO(eevee_film)
.specialization_constant(Type::INT, "samples_len", 0)
.specialization_constant(Type::BOOL, "use_reprojection", false)
.specialization_constant(Type::INT, "scaling_factor", 1)
.specialization_constant(Type::INT, "combined_id", 0)
.specialization_constant(Type::INT, "display_id", -1)
.specialization_constant(Type::INT, "normal_id", -1)
.additional_info("eevee_shared")
.additional_info("eevee_global_ubo")
.additional_info("eevee_velocity_camera")

View File

@ -182,6 +182,7 @@ static void OVERLAY_cache_init(void *vedata)
OVERLAY_edit_lattice_cache_init(data);
break;
case CTX_MODE_PAINT_GREASE_PENCIL:
case CTX_MODE_SCULPT_GREASE_PENCIL:
case CTX_MODE_EDIT_GREASE_PENCIL:
OVERLAY_edit_grease_pencil_cache_init(data);
break;

View File

@ -225,8 +225,9 @@ struct SortedFaceData {
Array<int> tris_num_by_material;
/**
* The first triangle index for each face, sorted into slices by material.
* May be empty if the mesh only has a single material.
*/
Array<int> face_tri_offsets;
std::optional<Array<int>> face_tri_offsets;
};
/**

View File

@ -611,7 +611,6 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph,
*/
const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
GPU_use_hq_normals_workaround();
const bool override_single_mat = mesh_render_mat_len_get(object, mesh) <= 1;
/* Create an array containing all the extractors that needs to be executed. */
ExtractorRunDatas extractors;
@ -621,8 +620,7 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph,
#define EXTRACT_ADD_REQUESTED(type, name) \
do { \
if (DRW_##type##_requested(mbuflist->type.name)) { \
const MeshExtract *extractor = mesh_extract_override_get( \
&extract_##name, do_hq_normals, override_single_mat); \
const MeshExtract *extractor = mesh_extract_override_get(&extract_##name, do_hq_normals); \
extractors.append(extractor); \
} \
} while (0)

View File

@ -189,9 +189,10 @@ static void accumululate_material_counts_mesh(
const MeshRenderData &mr, threading::EnumerableThreadSpecific<Array<int>> &all_tri_counts)
{
const OffsetIndices faces = mr.faces;
if (mr.material_indices.is_empty()) {
if (mr.use_hide && !mr.hide_poly.is_empty()) {
const Span hide_poly = mr.hide_poly;
const Span<bool> hide_poly = mr.hide_poly;
const Span material_indices = mr.material_indices;
if (material_indices.is_empty()) {
if (!hide_poly.is_empty()) {
all_tri_counts.local().first() = threading::parallel_reduce(
faces.index_range(),
4096,
@ -212,13 +213,12 @@ static void accumululate_material_counts_mesh(
return;
}
const Span material_indices = mr.material_indices;
threading::parallel_for(material_indices.index_range(), 1024, [&](const IndexRange range) {
Array<int> &tri_counts = all_tri_counts.local();
const int last_index = tri_counts.size() - 1;
if (mr.use_hide && !mr.hide_poly.is_empty()) {
if (!hide_poly.is_empty()) {
for (const int i : range) {
if (!mr.hide_poly[i]) {
if (!hide_poly[i]) {
const int mat = std::clamp(material_indices[i], 0, last_index);
tri_counts[mat] += bke::mesh::face_triangles_num(faces[i].size());
}
@ -257,64 +257,109 @@ static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
return std::move(tris_num_by_material);
}
static void mesh_render_data_faces_sorted_build(MeshRenderData &mr, MeshBufferCache &cache)
static Array<int> calc_face_tri_starts_bmesh(const MeshRenderData &mr,
MutableSpan<int> material_tri_starts)
{
cache.face_sorted.tris_num_by_material = mesh_render_data_mat_tri_len_build(mr);
const Span<int> tris_num_by_material = cache.face_sorted.tris_num_by_material;
BMesh &bm = *mr.bm;
Array<int> face_tri_offsets(bm.totface);
#ifndef NDEBUG
face_tri_offsets.fill(-1);
#endif
/* Apply offset. */
int visible_tris_num = 0;
Array<int, 32> mat_tri_offs(mr.materials_num);
{
for (int i = 0; i < mr.materials_num; i++) {
mat_tri_offs[i] = visible_tris_num;
visible_tris_num += tris_num_by_material[i];
const int mat_last = mr.materials_num - 1;
BMIter iter;
BMFace *face;
int i;
BM_ITER_MESH_INDEX (face, &iter, &bm, BM_FACES_OF_MESH, i) {
if (BM_elem_flag_test(face, BM_ELEM_HIDDEN)) {
continue;
}
const int mat = std::clamp(int(face->mat_nr), 0, mat_last);
face_tri_offsets[i] = material_tri_starts[mat];
material_tri_starts[mat] += face->len - 2;
}
cache.face_sorted.visible_tris_num = visible_tris_num;
cache.face_sorted.face_tri_offsets.reinitialize(mr.faces_num);
MutableSpan<int> face_tri_offsets = cache.face_sorted.face_tri_offsets;
return face_tri_offsets;
}
static bool mesh_is_single_material(const OffsetIndices<int> material_tri_starts)
{
const int used_materials = std::count_if(
material_tri_starts.index_range().begin(),
material_tri_starts.index_range().end(),
[&](const int i) { return material_tri_starts[i].size() > 0; });
return used_materials == 1;
}
static std::optional<Array<int>> calc_face_tri_starts_mesh(const MeshRenderData &mr,
MutableSpan<int> material_tri_starts)
{
const bool single_material = mesh_is_single_material(material_tri_starts.as_span());
if (single_material && mr.hide_poly.is_empty()) {
return std::nullopt;
}
const OffsetIndices faces = mr.faces;
const Span<bool> hide_poly = mr.hide_poly;
Array<int> face_tri_offsets(faces.size());
#ifndef NDEBUG
face_tri_offsets.fill(-1);
#endif
if (single_material) {
int offset = 0;
for (const int face : faces.index_range()) {
if (hide_poly[face]) {
continue;
}
face_tri_offsets[face] = offset;
offset += bke::mesh::face_triangles_num(faces[face].size());
}
return face_tri_offsets;
}
const Span<int> material_indices = mr.material_indices;
const int mat_last = mr.materials_num - 1;
for (const int face : faces.index_range()) {
if (!hide_poly.is_empty() && hide_poly[face]) {
continue;
}
const int mat = std::clamp(material_indices[face], 0, mat_last);
face_tri_offsets[face] = material_tri_starts[mat];
material_tri_starts[mat] += bke::mesh::face_triangles_num(faces[face].size());
}
return face_tri_offsets;
}
static SortedFaceData mesh_render_data_faces_sorted_build(const MeshRenderData &mr)
{
SortedFaceData cache;
cache.tris_num_by_material = mesh_render_data_mat_tri_len_build(mr);
const Span<int> tris_num_by_material = cache.tris_num_by_material;
Array<int, 32> material_tri_starts(mr.materials_num + 1);
material_tri_starts.as_mutable_span().drop_back(1).copy_from(tris_num_by_material);
offset_indices::accumulate_counts_to_offsets(material_tri_starts);
cache.visible_tris_num = material_tri_starts.last();
/* Sort per material. */
int mat_last = mr.materials_num - 1;
if (mr.extract_type == MR_EXTRACT_BMESH) {
BMIter iter;
BMFace *f;
int i;
BM_ITER_MESH_INDEX (f, &iter, mr.bm, BM_FACES_OF_MESH, i) {
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
const int mat = clamp_i(f->mat_nr, 0, mat_last);
face_tri_offsets[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += f->len - 2;
}
else {
face_tri_offsets[i] = -1;
}
}
cache.face_tri_offsets = calc_face_tri_starts_bmesh(mr, material_tri_starts);
}
else {
for (int i = 0; i < mr.faces_num; i++) {
if (!(mr.use_hide && !mr.hide_poly.is_empty() && mr.hide_poly[i])) {
const int mat = mr.material_indices.is_empty() ?
0 :
clamp_i(mr.material_indices[i], 0, mat_last);
face_tri_offsets[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += mr.faces[i].size() - 2;
}
else {
face_tri_offsets[i] = -1;
}
}
cache.face_tri_offsets = calc_face_tri_starts_mesh(mr, material_tri_starts);
}
return cache;
}
static void mesh_render_data_faces_sorted_ensure(MeshRenderData &mr, MeshBufferCache &cache)
{
if (!cache.face_sorted.face_tri_offsets.is_empty()) {
if (cache.face_sorted.visible_tris_num > 0) {
return;
}
mesh_render_data_faces_sorted_build(mr, cache);
cache.face_sorted = mesh_render_data_faces_sorted_build(mr);
}
void mesh_render_data_update_faces_sorted(MeshRenderData &mr,

View File

@ -572,8 +572,7 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob)
gps->hardness = brush->gpencil_settings->hardness;
copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio);
/* Reduce slightly the opacity of fill to make easy fill areas while drawing. */
gps->fill_opacity_fac = 0.8f;
gps->fill_opacity_fac = gpd->runtime.fill_opacity_fac;
gps->tot_triangles = max_ii(0, gpd->runtime.sbuffer_used - 2);
gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND;

View File

@ -47,8 +47,8 @@ void Manager::begin_sync()
acquired_textures.clear();
layer_attributes.clear();
// For some reason, if this uninitialised data pattern was enabled (ie release asserts enabled),
// The viewport just gives up rendering objects on ARM64 devices. Possibly Mesa GLOn12-related.
/* For some reason, if this uninitialized data pattern was enabled (ie release asserts enabled),
* The viewport just gives up rendering objects on ARM64 devices. Possibly Mesa GLOn12-related. */
#if !defined(NDEBUG) && !defined(_M_ARM64)
/* Detect uninitialized data. */
memset(matrix_buf.current().data(),

View File

@ -64,26 +64,13 @@ static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *ex
return extractor;
}
static const MeshExtract *mesh_extract_override_single_material(const MeshExtract *extractor)
{
if (extractor == &extract_tris) {
return &extract_tris_single_mat;
}
return extractor;
}
const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
const bool do_hq_normals,
const bool do_single_mat)
const bool do_hq_normals)
{
if (do_hq_normals) {
extractor = mesh_extract_override_hq_normals(extractor);
}
if (do_single_mat) {
extractor = mesh_extract_override_single_material(extractor);
}
return extractor;
}

View File

@ -320,9 +320,7 @@ struct EditLoopData {
void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferList *mbuflist);
eMRIterType mesh_extract_iter_type(const MeshExtract *ext);
const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
bool do_hq_normals,
bool do_single_mat);
const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, bool do_hq_normals);
void mesh_render_data_face_flag(const MeshRenderData &mr,
const BMFace *efa,
BMUVOffsets offsets,
@ -340,7 +338,6 @@ template<typename GPUType>
void extract_vert_normals(const MeshRenderData &mr, MutableSpan<GPUType> normals);
extern const MeshExtract extract_tris;
extern const MeshExtract extract_tris_single_mat;
extern const MeshExtract extract_lines;
extern const MeshExtract extract_lines_with_lines_loose;
extern const MeshExtract extract_lines_loose_only;

View File

@ -16,83 +16,84 @@
namespace blender::draw {
static void extract_tris_mat_task_reduce(void *_userdata_to, void *_userdata_from)
{
GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
GPU_indexbuf_join(elb_to, elb_from);
}
/* ---------------------------------------------------------------------- */
/** \name Extract Triangles Indices (multi material)
* \{ */
static void extract_tris_init(const MeshRenderData &mr,
MeshBatchCache & /*cache*/,
void * /*ibo*/,
void *tls_data)
static void extract_tris_mesh(const MeshRenderData &mr, gpu::IndexBuf &ibo)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.face_sorted->visible_tris_num, mr.corners_num);
}
static void extract_tris_iter_face_bm(const MeshRenderData &mr,
const BMFace *f,
const int f_index,
void *_data)
{
int tri_offset = mr.face_sorted->face_tri_offsets[f_index];
if (tri_offset == -1) {
const Span<int3> corner_tris = mr.corner_tris;
if (!mr.face_sorted->face_tri_offsets) {
/* There are no hidden faces and no reordering is necessary to group triangles with the same
* material. The corner indices from #Mesh::corner_tris() can be copied directly to the GPU. */
BLI_assert(mr.face_sorted->visible_tris_num == corner_tris.size());
GPU_indexbuf_build_in_place_from_memory(&ibo,
GPU_PRIM_TRIS,
corner_tris.cast<uint32_t>().data(),
corner_tris.size(),
0,
mr.corners_num,
false);
return;
}
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
int tri_first_index_real = poly_to_tri_count(f_index, BM_elem_index_get(f->l_first));
const OffsetIndices faces = mr.faces;
const Span<bool> hide_poly = mr.hide_poly;
Span<std::array<BMLoop *, 3>> looptris = mr.edit_bmesh->looptris;
int tri_len = f->len - 2;
for (int offs = 0; offs < tri_len; offs++) {
const std::array<BMLoop *, 3> &elt = looptris[tri_first_index_real + offs];
int tri_index = tri_offset + offs;
GPU_indexbuf_set_tri_verts(elb,
tri_index,
BM_elem_index_get(elt[0]),
BM_elem_index_get(elt[1]),
BM_elem_index_get(elt[2]));
}
GPUIndexBufBuilder builder;
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, mr.face_sorted->visible_tris_num, mr.corners_num);
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
const Span<int> face_tri_offsets = mr.face_sorted->face_tri_offsets->as_span();
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
for (const int face : range) {
if (!hide_poly.is_empty() && hide_poly[face]) {
continue;
}
const IndexRange mesh_range = bke::mesh::face_triangles_range(faces, face);
const Span<uint3> mesh_tris = corner_tris.slice(mesh_range).cast<uint3>();
MutableSpan<uint3> ibo_tris = data.slice(face_tri_offsets[face], mesh_tris.size());
ibo_tris.copy_from(mesh_tris);
}
});
GPU_indexbuf_build_in_place_ex(&builder, 0, mr.face_sorted->visible_tris_num, false, &ibo);
}
static void extract_tris_iter_face_mesh(const MeshRenderData &mr,
const int face_index,
void *_data)
static void extract_tris_bmesh(const MeshRenderData &mr, gpu::IndexBuf &ibo)
{
int tri_offset = mr.face_sorted->face_tri_offsets[face_index];
if (tri_offset == -1) {
return;
}
GPUIndexBufBuilder builder;
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, mr.face_sorted->visible_tris_num, mr.corners_num);
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
const IndexRange face = mr.faces[face_index];
BMesh &bm = *mr.bm;
const Span<std::array<BMLoop *, 3>> looptris = mr.edit_bmesh->looptris;
const Span<int> face_tri_offsets = *mr.face_sorted->face_tri_offsets;
threading::parallel_for(IndexRange(bm.totface), 1024, [&](const IndexRange range) {
for (const int face_index : range) {
const BMFace &face = *BM_face_at_index(&bm, face_index);
if (BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
continue;
}
const int loop_index = BM_elem_index_get(BM_FACE_FIRST_LOOP(&face));
const IndexRange bm_tris(poly_to_tri_count(face_index, loop_index),
bke::mesh::face_triangles_num(face.len));
const IndexRange ibo_tris(face_tri_offsets[face_index], bm_tris.size());
for (const int i : bm_tris.index_range()) {
data[ibo_tris[i]] = uint3(BM_elem_index_get(looptris[bm_tris[i]][0]),
BM_elem_index_get(looptris[bm_tris[i]][1]),
BM_elem_index_get(looptris[bm_tris[i]][2]));
}
}
});
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
int tri_first_index_real = poly_to_tri_count(face_index, face.start());
int tri_len = face.size() - 2;
for (int offs = 0; offs < tri_len; offs++) {
const int3 &tri = mr.corner_tris[tri_first_index_real + offs];
int tri_index = tri_offset + offs;
GPU_indexbuf_set_tri_verts(elb, tri_index, tri[0], tri[1], tri[2]);
}
GPU_indexbuf_build_in_place_ex(&builder, 0, mr.face_sorted->visible_tris_num, false, &ibo);
}
static void extract_tris_finish(const MeshRenderData &mr,
MeshBatchCache &cache,
void *buf,
void *_data)
gpu::IndexBuf &ibo)
{
gpu::IndexBuf *ibo = static_cast<gpu::IndexBuf *>(buf);
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
GPU_indexbuf_build_in_place(elb, ibo);
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
if (mr.use_final_mesh && cache.tris_per_mat) {
@ -107,12 +108,29 @@ static void extract_tris_finish(const MeshRenderData &mr,
/* Multiply by 3 because these are triangle indices. */
const int start = mat_start * 3;
const int len = mat_tri_len * 3;
GPU_indexbuf_create_subrange_in_place(cache.tris_per_mat[i], ibo, start, len);
GPU_indexbuf_create_subrange_in_place(cache.tris_per_mat[i], &ibo, start, len);
mat_start += mat_tri_len;
}
}
}
static void extract_tris_init(const MeshRenderData &mr,
MeshBatchCache &cache,
void *ibo_v,
void * /*tls_data*/)
{
gpu::IndexBuf &ibo = *static_cast<gpu::IndexBuf *>(ibo_v);
if (mr.extract_type == MR_EXTRACT_MESH) {
extract_tris_mesh(mr, ibo);
}
else {
extract_tris_bmesh(mr, ibo);
}
extract_tris_finish(mr, cache, ibo);
}
static void extract_tris_init_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData & /*mr*/,
MeshBatchCache &cache,
@ -144,101 +162,7 @@ constexpr MeshExtract create_extractor_tris()
MeshExtract extractor = {nullptr};
extractor.init = extract_tris_init;
extractor.init_subdiv = extract_tris_init_subdiv;
extractor.iter_face_bm = extract_tris_iter_face_bm;
extractor.iter_face_mesh = extract_tris_iter_face_mesh;
extractor.task_reduce = extract_tris_mat_task_reduce;
extractor.finish = extract_tris_finish;
extractor.data_type = MR_DATA_CORNER_TRI | MR_DATA_POLYS_SORTED;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris);
return extractor;
}
/** \} */
/** \name Extract Triangles Indices (single material)
* \{ */
static void extract_tris_single_mat_init(const MeshRenderData &mr,
MeshBatchCache & /*cache*/,
void * /*ibo*/,
void *tls_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.corner_tris_num, mr.corners_num);
}
static void extract_tris_single_mat_iter_looptri_bm(const MeshRenderData & /*mr*/,
BMLoop **elt,
const int elt_index,
void *_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
GPU_indexbuf_set_tri_verts(elb,
elt_index,
BM_elem_index_get(elt[0]),
BM_elem_index_get(elt[1]),
BM_elem_index_get(elt[2]));
}
else {
GPU_indexbuf_set_tri_restart(elb, elt_index);
}
}
static void extract_tris_single_mat_iter_corner_tri_mesh(const MeshRenderData &mr,
const int3 &tri,
const int tri_index,
void *_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
const int face_i = mr.corner_tri_faces[tri_index];
const bool hidden = mr.use_hide && !mr.hide_poly.is_empty() && mr.hide_poly[face_i];
if (hidden) {
GPU_indexbuf_set_tri_restart(elb, tri_index);
}
else {
GPU_indexbuf_set_tri_verts(elb, tri_index, tri[0], tri[1], tri[2]);
}
}
static void extract_tris_single_mat_finish(const MeshRenderData &mr,
MeshBatchCache &cache,
void *buf,
void *_data)
{
gpu::IndexBuf *ibo = static_cast<gpu::IndexBuf *>(buf);
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
GPU_indexbuf_build_in_place(elb, ibo);
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
if (mr.use_final_mesh && cache.tris_per_mat) {
for (int i = 0; i < mr.materials_num; i++) {
/* These IBOs have not been queried yet but we create them just in case they are needed
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
if (cache.tris_per_mat[i] == nullptr) {
cache.tris_per_mat[i] = GPU_indexbuf_calloc();
}
/* Multiply by 3 because these are triangle indices. */
const int len = mr.corner_tris_num * 3;
GPU_indexbuf_create_subrange_in_place(cache.tris_per_mat[i], ibo, 0, len);
}
}
}
constexpr MeshExtract create_extractor_tris_single_mat()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_tris_single_mat_init;
extractor.init_subdiv = extract_tris_init_subdiv;
extractor.iter_looptri_bm = extract_tris_single_mat_iter_looptri_bm;
extractor.iter_corner_tri_mesh = extract_tris_single_mat_iter_corner_tri_mesh;
extractor.task_reduce = extract_tris_mat_task_reduce;
extractor.finish = extract_tris_single_mat_finish;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris);
return extractor;
@ -247,6 +171,5 @@ constexpr MeshExtract create_extractor_tris_single_mat()
/** \} */
const MeshExtract extract_tris = create_extractor_tris();
const MeshExtract extract_tris_single_mat = create_extractor_tris_single_mat();
} // namespace blender::draw

View File

@ -111,18 +111,36 @@ static void extract_normals_mesh(const MeshRenderData &mr, MutableSpan<GPUType>
template<typename GPUType>
static void extract_paint_overlay_flags(const MeshRenderData &mr, MutableSpan<GPUType> normals)
{
if (mr.select_poly.is_empty() && mr.hide_poly.is_empty() && (!mr.edit_bmesh || !mr.v_origindex))
{
const bool use_face_select = (mr.mesh->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
Span<bool> selection;
if (mr.mesh->editflag & ME_EDIT_PAINT_FACE_SEL) {
selection = mr.select_poly;
}
else if (mr.mesh->editflag & ME_EDIT_PAINT_VERT_SEL) {
selection = mr.select_vert;
}
if (selection.is_empty() && mr.hide_poly.is_empty() && (!mr.edit_bmesh || !mr.v_origindex)) {
return;
}
const OffsetIndices faces = mr.faces;
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
if (!mr.select_poly.is_empty()) {
const Span<bool> select_poly = mr.select_poly;
for (const int face : range) {
if (select_poly[face]) {
if (!selection.is_empty()) {
if (use_face_select) {
for (const int face : range) {
if (selection[face]) {
for (const int corner : faces[face]) {
normals[corner].w = 1;
}
}
}
}
else {
const Span<int> corner_verts = mr.corner_verts;
for (const int face : range) {
for (const int corner : faces[face]) {
normals[corner].w = 1;
if (selection[corner_verts[corner]]) {
normals[corner].w = 1;
}
}
}
}

View File

@ -81,27 +81,26 @@ void draw_keyframe_shape(const float x,
if (draw_fill) {
/* get interior colors from theme (for selected and unselected only) */
switch (key_type) {
case BEZT_KEYTYPE_BREAKDOWN: /* bluish frames (default theme) */
UI_GetThemeColor4ubv(sel ? TH_KEYTYPE_BREAKDOWN_SELECT : TH_KEYTYPE_BREAKDOWN, fill_col);
case BEZT_KEYTYPE_BREAKDOWN:
UI_GetThemeColor3ubv(sel ? TH_KEYTYPE_BREAKDOWN_SELECT : TH_KEYTYPE_BREAKDOWN, fill_col);
break;
case BEZT_KEYTYPE_EXTREME: /* reddish frames (default theme) */
UI_GetThemeColor4ubv(sel ? TH_KEYTYPE_EXTREME_SELECT : TH_KEYTYPE_EXTREME, fill_col);
case BEZT_KEYTYPE_EXTREME:
UI_GetThemeColor3ubv(sel ? TH_KEYTYPE_EXTREME_SELECT : TH_KEYTYPE_EXTREME, fill_col);
break;
case BEZT_KEYTYPE_JITTER: /* greenish frames (default theme) */
UI_GetThemeColor4ubv(sel ? TH_KEYTYPE_JITTER_SELECT : TH_KEYTYPE_JITTER, fill_col);
case BEZT_KEYTYPE_JITTER:
UI_GetThemeColor3ubv(sel ? TH_KEYTYPE_JITTER_SELECT : TH_KEYTYPE_JITTER, fill_col);
break;
case BEZT_KEYTYPE_MOVEHOLD: /* similar to traditional keyframes, but different... */
UI_GetThemeColor4ubv(sel ? TH_KEYTYPE_MOVEHOLD_SELECT : TH_KEYTYPE_MOVEHOLD, fill_col);
case BEZT_KEYTYPE_MOVEHOLD:
UI_GetThemeColor3ubv(sel ? TH_KEYTYPE_MOVEHOLD_SELECT : TH_KEYTYPE_MOVEHOLD, fill_col);
break;
case BEZT_KEYTYPE_KEYFRAME: /* traditional yellowish frames (default theme) */
UI_GetThemeColor4ubv(sel ? TH_KEYTYPE_KEYFRAME_SELECT : TH_KEYTYPE_KEYFRAME, fill_col);
case BEZT_KEYTYPE_KEYFRAME:
UI_GetThemeColor3ubv(sel ? TH_KEYTYPE_KEYFRAME_SELECT : TH_KEYTYPE_KEYFRAME, fill_col);
break;
}
/* NOTE: we don't use the straight alpha from the theme, or else effects such as
* graying out protected/muted channels doesn't work correctly!
*/
fill_col[3] *= alpha;
/* For effects like graying out protected/muted channels. The theme RNA/UI doesn't allow users
* to set the alpha. */
fill_col[3] = 255.0f * alpha;
if (!draw_outline) {
/* force outline color to match */

View File

@ -352,10 +352,10 @@ void storage_tag_main_data_dirty()
}
}
void storage_id_remap(ID *id_new, ID *id_old)
void storage_id_remap(ID *id_old, ID *id_new)
{
for (AssetList &list : global_storage().values()) {
list.remap_id(id_new, id_old);
list.remap_id(id_old, id_new);
}
}

View File

@ -1365,6 +1365,8 @@ static int exec(bContext *C, wmOperator * /*op*/)
attributes.remove("cyclic");
}
curves.calculate_bezier_auto_handles();
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
@ -1525,6 +1527,64 @@ static void CURVES_OT_subdivide(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
namespace set_handle_type {
static int exec(bContext *C, wmOperator *op)
{
const HandleType dst_handle_type = HandleType(RNA_enum_get(op->ptr, "type"));
for (Curves *curves_id : get_unique_editable_curves(*C)) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const VArraySpan<bool> selection = *attributes.lookup_or_default<bool>(
".selection", bke::AttrDomain::Point, true);
const VArraySpan<bool> selection_left = *attributes.lookup_or_default<bool>(
".selection_handle_left", bke::AttrDomain::Point, true);
const VArraySpan<bool> selection_right = *attributes.lookup_or_default<bool>(
".selection_handle_right", bke::AttrDomain::Point, true);
MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
threading::parallel_for(curves.points_range(), 4096, [&](const IndexRange range) {
for (const int point_i : range) {
if (selection_left[point_i] || selection[point_i]) {
handle_types_left[point_i] = int8_t(dst_handle_type);
}
if (selection_right[point_i] || selection[point_i]) {
handle_types_right[point_i] = int8_t(dst_handle_type);
}
}
});
curves.calculate_bezier_auto_handles();
curves.tag_positions_changed();
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
return OPERATOR_FINISHED;
}
} // namespace set_handle_type
static void CURVES_OT_handle_type_set(wmOperatorType *ot)
{
ot->name = "Set Handle Type";
ot->idname = __func__;
ot->description = "Set the handle type for bezier curves";
ot->invoke = WM_menu_invoke;
ot->exec = set_handle_type::exec;
ot->poll = editable_curves_in_edit_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(
ot->srna, "type", rna_enum_curves_handle_type_items, CURVE_TYPE_POLY, "Type", nullptr);
}
void operatortypes_curves()
{
WM_operatortype_append(CURVES_OT_attribute_set);
@ -1548,6 +1608,7 @@ void operatortypes_curves()
WM_operatortype_append(CURVES_OT_curve_type_set);
WM_operatortype_append(CURVES_OT_switch_direction);
WM_operatortype_append(CURVES_OT_subdivide);
WM_operatortype_append(CURVES_OT_handle_type_set);
}
void operatormacros_curves()

View File

@ -13,6 +13,7 @@
#include <cstdlib>
#include <cstring>
#include "DNA_object_enums.h"
#include "MEM_guardedalloc.h"
#include "BLI_ghash.h"
@ -25,6 +26,7 @@
#include "BLT_translation.hh"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@ -61,6 +63,7 @@
#include "UI_view2d.hh"
#include "ED_gpencil_legacy.hh"
#include "ED_image.hh"
#include "ED_object.hh"
#include "ED_outliner.hh"
#include "ED_screen.hh"
@ -453,10 +456,25 @@ static bool gpencil_sculptmode_toggle_poll(bContext *C)
{
/* if using gpencil object, use this gpd */
Object *ob = CTX_data_active_object(C);
if ((ob) && (ob->type == OB_GPENCIL_LEGACY)) {
if (ob == nullptr) {
return false;
}
if (ELEM(ob->type, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL)) {
return ob->data != nullptr;
}
return ED_gpencil_data_get_active(C) != nullptr;
return false;
}
static bool gpencil_sculpt_poll_view3d(bContext *C)
{
const Object *ob = CTX_data_active_object(C);
if (ob == nullptr || (ob->mode & OB_MODE_SCULPT_GPENCIL_LEGACY) == 0) {
return false;
}
if (CTX_wm_region_view3d(C) == nullptr) {
return false;
}
return true;
}
static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
@ -467,35 +485,49 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
const bool back = RNA_boolean_get(op->ptr, "back");
wmMsgBus *mbus = CTX_wm_message_bus(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
bool is_object = false;
short mode;
/* if using a gpencil object, use this datablock */
Object *ob = CTX_data_active_object(C);
if ((ob) && (ob->type == OB_GPENCIL_LEGACY)) {
gpd = static_cast<bGPdata *>(ob->data);
bGPdata *gpd = ED_gpencil_data_get_active(C);
if (gpd == nullptr) {
return OPERATOR_CANCELLED;
}
/* Just toggle sculptmode flag... */
gpd->flag ^= GP_DATA_STROKE_SCULPTMODE;
/* set mode */
if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) {
mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
}
else {
/* try to back previous mode */
if ((ob->restore_mode) && (back == 1)) {
mode = ob->restore_mode;
}
else {
mode = OB_MODE_OBJECT;
}
}
is_object = true;
}
if ((ob) && (ob->type == OB_GREASE_PENCIL)) {
const bool is_mode_set = (ob->mode & OB_MODE_SCULPT_GPENCIL_LEGACY) != 0;
if (is_mode_set) {
mode = OB_MODE_OBJECT;
}
else {
Scene *scene = CTX_data_scene(C);
BKE_paint_init(
bmain, scene, PaintMode::SculptGreasePencil, PAINT_CURSOR_SCULPT_GREASE_PENCIL);
Paint *paint = BKE_paint_get_active_from_paintmode(scene, PaintMode::SculptGreasePencil);
ED_paint_cursor_start(paint, gpencil_sculpt_poll_view3d);
mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
}
is_object = true;
}
if (gpd == nullptr) {
return OPERATOR_CANCELLED;
}
/* Just toggle sculptmode flag... */
gpd->flag ^= GP_DATA_STROKE_SCULPTMODE;
/* set mode */
if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) {
mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
}
else {
mode = OB_MODE_OBJECT;
}
if (is_object) {
/* try to back previous mode */
if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) {
mode = ob->restore_mode;
}
ob->restore_mode = ob->mode;
ob->mode = mode;
}
@ -511,9 +543,16 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
}
/* setup other modes */
ED_gpencil_setup_modes(C, gpd, mode);
/* set cache as dirty */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
if (ob->type == OB_GPENCIL_LEGACY) {
bGPdata *gpd = ED_gpencil_data_get_active(C);
ED_gpencil_setup_modes(C, gpd, mode);
/* set cache as dirty */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
if (ob->type == OB_GREASE_PENCIL) {
GreasePencil *grease_pencil = static_cast<GreasePencil *>(ob->data);
DEG_id_tag_update(&grease_pencil->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, nullptr);
WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);

View File

@ -2040,6 +2040,14 @@ static void gpencil_init_colors(tGPsdata *p)
gpd->runtime.matid = BKE_object_material_slot_find_index(p->ob, p->material);
gpd->runtime.sbuffer_brush = brush;
/* Reduce slightly the opacity of fill to make easy fill areas while drawing. */
gpd->runtime.fill_opacity_fac = 0.8f;
if ((brush->gpencil_settings->flag & GP_BRUSH_DISSABLE_LASSO) != 0) {
/* Don't set it to 0 so that there is still some feedback if the material has no stroke color.
*/
gpd->runtime.fill_opacity_fac = 0.1f;
}
}
/* (re)init new painting data */

View File

@ -3019,6 +3019,7 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph,
if (gpd_eval != nullptr) {
copy_v4_v4(gpd_eval->runtime.vert_color_fill, gpd->runtime.vert_color_fill);
gpd_eval->runtime.matid = gpd->runtime.matid;
gpd_eval->runtime.fill_opacity_fac = gpd->runtime.fill_opacity_fac;
}
}

View File

@ -2239,12 +2239,15 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
/* Deselect everything in the target layer. The pasted strokes are the only ones then after the
* paste. That's convenient for the user. */
bke::GSpanAttributeWriter selection_in_target = ed::curves::ensure_selection_attribute(
target_drawing->strokes_for_write(), selection_domain, CD_PROP_BOOL);
ed::curves::fill_selection_false(selection_in_target.span);
selection_in_target.finish();
/* Deselect everything from editable drawings. The pasted strokes are the only ones then after
* the paste. That's convenient for the user. */
const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::GSpanAttributeWriter selection_in_target = ed::curves::ensure_selection_attribute(
info.drawing.strokes_for_write(), selection_domain, CD_PROP_BOOL);
ed::curves::fill_selection_false(selection_in_target.span);
selection_in_target.finish();
});
/* Get a list of all materials in the scene. */
Map<uint, Material *> scene_materials;
@ -2354,6 +2357,7 @@ static int grease_pencil_copy_strokes_exec(bContext *C, wmOperator *op)
}
if (!anything_copied) {
clipboard.curves.resize(0, 0);
return OPERATOR_CANCELLED;
}

View File

@ -167,7 +167,7 @@ bool duplicate_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::L
/* Make a copy of the frame in the duplicates. */
GreasePencilFrame frame_duplicate = frame;
frame_duplicate.drawing_index = duplicated_drawing_index;
trans_data.temp_frames_buffer.add_overwrite(frame_number, frame_duplicate);
trans_data.duplicated_frames_buffer.add_overwrite(frame_number, frame_duplicate);
/* Deselect the current frame, so that only the copy is selected. */
frame.flag ^= GP_FRAME_SELECTED;

View File

@ -8,6 +8,7 @@
#include "BKE_context.hh"
#include "DNA_object_enums.h"
#include "DNA_scene_types.h"
#include "ED_grease_pencil.hh"
@ -81,6 +82,22 @@ bool grease_pencil_painting_poll(bContext *C)
return true;
}
bool grease_pencil_sculpting_poll(bContext *C)
{
if (!active_grease_pencil_poll(C)) {
return false;
}
Object *object = CTX_data_active_object(C);
if ((object->mode & OB_MODE_SCULPT_GPENCIL_LEGACY) == 0) {
return false;
}
ToolSettings *ts = CTX_data_tool_settings(C);
if (!ts || !ts->gp_sculptpaint) {
return false;
}
return true;
}
static void keymap_grease_pencil_edit_mode(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(

View File

@ -175,6 +175,7 @@ bool editable_grease_pencil_poll(bContext *C);
bool active_grease_pencil_layer_poll(bContext *C);
bool editable_grease_pencil_point_selection_poll(bContext *C);
bool grease_pencil_painting_poll(bContext *C);
bool grease_pencil_sculpting_poll(bContext *C);
struct DrawingInfo {
const bke::greasepencil::Drawing &drawing;

View File

@ -1066,18 +1066,21 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
void UI_theme_init_default()
{
/* we search for the theme with name Default */
/* We search for the theme with the default name. */
bTheme *btheme = static_cast<bTheme *>(
BLI_findstring(&U.themes, "Default", offsetof(bTheme, name)));
BLI_findstring(&U.themes, U_theme_default.name, offsetof(bTheme, name)));
if (btheme == nullptr) {
btheme = MEM_cnew<bTheme>(__func__);
BLI_addtail(&U.themes, btheme);
BLI_addhead(&U.themes, btheme);
}
/* Must be first, see `U.themes` doc-string. */
BLI_listbase_rotate_first(&U.themes, btheme);
UI_SetTheme(0, 0); /* make sure the global used in this file is set */
const int active_theme_area = btheme->active_theme_area;
memcpy(btheme, &U_theme_default, sizeof(*btheme));
MEMCPY_STRUCT_AFTER(btheme, &U_theme_default, name);
btheme->active_theme_area = active_theme_area;
}

Some files were not shown because too many files have changed in this diff Show More