Anim: Per bone wire width for custom shapes #120176
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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 ******************************/
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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")
|
||||
|
||||
# --------------------------------------------------------
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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],
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -50,7 +50,6 @@ namespace blender::animrig {
|
|||
|
||||
CombinedKeyingResult::CombinedKeyingResult()
|
||||
{
|
||||
result_counter = Array<int>(int(SingleKeyingResult::_KEYING_RESULT_MAX));
|
||||
result_counter.fill(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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")))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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_);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))) {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue