Vulkan: Push constants #104880

Merged
Jeroen Bakker merged 73 commits from Jeroen-Bakker/blender:vulkan-push-constants into main 2023-03-06 12:29:06 +01:00
57 changed files with 931 additions and 433 deletions
Showing only changes of commit 2e9062e4d4 - Show all commits

View File

@ -29,7 +29,8 @@ class MetalDevice : public Device {
id<MTLArgumentEncoder> mtlAncillaryArgEncoder =
nil; /* encoder used for fetching device pointers from MTLBuffers */
string source[PSO_NUM];
string source_md5[PSO_NUM];
string kernels_md5[PSO_NUM];
string global_defines_md5[PSO_NUM];
bool capture_enabled = false;
@ -115,6 +116,8 @@ class MetalDevice : public Device {
bool use_local_atomic_sort() const;
string preprocess_source(MetalPipelineType pso_type, const uint kernel_features, string* source = nullptr);
bool make_source_and_check_if_compile_needed(MetalPipelineType pso_type);
void make_source(MetalPipelineType pso_type, const uint kernel_features);

View File

@ -310,7 +310,7 @@ bool MetalDevice::use_local_atomic_sort() const
return DebugFlags().metal.use_local_atomic_sort;
}
void MetalDevice::make_source(MetalPipelineType pso_type, const uint kernel_features)
string MetalDevice::preprocess_source(MetalPipelineType pso_type, const uint kernel_features, string* source)
{
string global_defines;
if (use_adaptive_compilation()) {
@ -353,70 +353,70 @@ void MetalDevice::make_source(MetalPipelineType pso_type, const uint kernel_feat
NSOperatingSystemVersion macos_ver = [processInfo operatingSystemVersion];
global_defines += "#define __KERNEL_METAL_MACOS__ " + to_string(macos_ver.majorVersion) + "\n";
string &source = this->source[pso_type];
source = "\n#include \"kernel/device/metal/kernel.metal\"\n";
source = path_source_replace_includes(source, path_get("source"));
/* Perform any required specialization on the source.
* With Metal function constants we can generate a single variant of the kernel source which can
* be repeatedly respecialized.
*/
string baked_constants;
/* Replace specific KernelData "dot" dereferences with a Metal function_constant identifier of
* the same character length. Build a string of all active constant values which is then hashed
* in order to identify the PSO.
*/
if (pso_type != PSO_GENERIC) {
const double starttime = time_dt();
if (source) {
const double starttime = time_dt();
# define KERNEL_STRUCT_BEGIN(name, parent) \
string_replace_same_length(source, "kernel_data." #parent ".", "kernel_data_" #parent "_");
string_replace_same_length(*source, "kernel_data." #parent ".", "kernel_data_" #parent "_");
bool next_member_is_specialized = true;
bool next_member_is_specialized = true;
# define KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE next_member_is_specialized = false;
/* Add constants to md5 so that 'get_best_pipeline' is able to return a suitable match. */
# define KERNEL_STRUCT_MEMBER(parent, _type, name) \
if (next_member_is_specialized) { \
baked_constants += string(#parent "." #name "=") + \
to_string(_type(launch_params.data.parent.name)) + "\n"; \
} \
else { \
string_replace( \
source, "kernel_data_" #parent "_" #name, "kernel_data." #parent ".__unused_" #name); \
next_member_is_specialized = true; \
}
if (!next_member_is_specialized) { \
string_replace( \
*source, "kernel_data_" #parent "_" #name, "kernel_data." #parent ".__unused_" #name); \
next_member_is_specialized = true; \
}
# include "kernel/data_template.h"
# undef KERNEL_STRUCT_MEMBER
# undef KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
# undef KERNEL_STRUCT_BEGIN
metal_printf("KernelData patching took %.1f ms\n", (time_dt() - starttime) * 1000.0);
}
/* Opt in to all of available specializations. This can be made more granular for the
* PSO_SPECIALIZED_INTERSECT case in order to minimize the number of specialization requests,
* but the overhead should be negligible as these are very quick to (re)build and aren't
* serialized to disk via MTLBinaryArchives.
*/
global_defines += "#define __KERNEL_USE_DATA_CONSTANTS__\n";
metal_printf("KernelData patching took %.1f ms\n", (time_dt() - starttime) * 1000.0);
}
source = global_defines + source;
# if 0
metal_printf("================\n%s================\n\%s================\n",
global_defines.c_str(),
baked_constants.c_str());
metal_printf("================\n%s================\n",
global_defines.c_str());
# endif
/* Generate an MD5 from the source and include any baked constants. This is used when caching
* PSOs. */
MD5Hash md5;
md5.append(baked_constants);
md5.append(source);
if (use_metalrt) {
md5.append(std::to_string(kernel_features & METALRT_FEATURE_MASK));
if (source) {
*source = global_defines + *source;
}
source_md5[pso_type] = md5.get_hex();
MD5Hash md5;
md5.append(global_defines);
return md5.get_hex();
}
void MetalDevice::make_source(MetalPipelineType pso_type, const uint kernel_features)
{
string &source = this->source[pso_type];
source = "\n#include \"kernel/device/metal/kernel.metal\"\n";
source = path_source_replace_includes(source, path_get("source"));
/* Perform any required specialization on the source.
* With Metal function constants we can generate a single variant of the kernel source which can
* be repeatedly respecialized.
*/
global_defines_md5[pso_type] = preprocess_source(pso_type, kernel_features, &source);
}
bool MetalDevice::load_kernels(const uint _kernel_features)
@ -450,9 +450,45 @@ bool MetalDevice::load_kernels(const uint _kernel_features)
bool MetalDevice::make_source_and_check_if_compile_needed(MetalPipelineType pso_type)
{
if (this->source[pso_type].empty()) {
string defines_md5 = preprocess_source(pso_type, kernel_features);
/* Rebuild the source string if the injected block of #defines has changed. */
if (global_defines_md5[pso_type] != defines_md5) {
make_source(pso_type, kernel_features);
}
string constant_values;
if (pso_type != PSO_GENERIC) {
bool next_member_is_specialized = true;
# define KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE next_member_is_specialized = false;
/* Add specialization constants to md5 so that 'get_best_pipeline' is able to return a suitable match. */
# define KERNEL_STRUCT_MEMBER(parent, _type, name) \
if (next_member_is_specialized) { \
constant_values += string(#parent "." #name "=") + \
to_string(_type(launch_params.data.parent.name)) + "\n"; \
} \
else { \
next_member_is_specialized = true; \
}
# include "kernel/data_template.h"
# undef KERNEL_STRUCT_MEMBER
# undef KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
# if 0
metal_printf("================\n%s================\n",
constant_values.c_str());
# endif
}
MD5Hash md5;
md5.append(constant_values);
md5.append(source[pso_type]);
kernels_md5[pso_type] = md5.get_hex();
return MetalDeviceKernels::should_load_kernels(this, pso_type);
}

View File

@ -76,7 +76,7 @@ struct MetalKernelPipeline {
id<MTLLibrary> mtlLibrary = nil;
MetalPipelineType pso_type;
string source_md5;
string kernels_md5;
size_t usage_count = 0;
KernelData kernel_data_;

View File

@ -292,7 +292,7 @@ bool ShaderCache::should_load_kernel(DeviceKernel device_kernel,
/* check whether the kernel has already been requested / cached */
thread_scoped_lock lock(cache_mutex);
for (auto &pipeline : pipelines[device_kernel]) {
if (pipeline->source_md5 == device->source_md5[pso_type]) {
if (pipeline->kernels_md5 == device->kernels_md5[pso_type]) {
return false;
}
}
@ -332,7 +332,7 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
memcpy(&pipeline->kernel_data_, &device->launch_params.data, sizeof(pipeline->kernel_data_));
pipeline->pso_type = pso_type;
pipeline->mtlDevice = mtlDevice;
pipeline->source_md5 = device->source_md5[pso_type];
pipeline->kernels_md5 = device->kernels_md5[pso_type];
pipeline->mtlLibrary = device->mtlLibrary[pso_type];
pipeline->device_kernel = device_kernel;
pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup;
@ -392,8 +392,8 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M
}
if (pipeline->pso_type != PSO_GENERIC) {
if (pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_INTERSECT] ||
pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_SHADE]) {
if (pipeline->kernels_md5 == device->kernels_md5[PSO_SPECIALIZED_INTERSECT] ||
pipeline->kernels_md5 == device->kernels_md5[PSO_SPECIALIZED_SHADE]) {
best_pipeline = pipeline.get();
}
}
@ -674,7 +674,7 @@ void MetalKernelPipeline::compile()
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
string osVersion = [[processInfo operatingSystemVersionString] UTF8String];
MD5Hash local_md5;
local_md5.append(source_md5);
local_md5.append(kernels_md5);
local_md5.append(osVersion);
local_md5.append((uint8_t *)&this->threads_per_threadgroup,
sizeof(this->threads_per_threadgroup));

View File

@ -1826,7 +1826,10 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
if (!window->m_mousePresent) {
WINTAB_PRINTF("HWND %p mouse enter\n", window->getHWND());
TRACKMOUSEEVENT tme = {sizeof(tme)};
tme.dwFlags = TME_LEAVE;
/* Request WM_MOUSELEAVE message when the cursor leaves the client area, and
* WM_MOUSEHOVER message after 50ms when in the client area. */
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.dwHoverTime = 50;
tme.hwndTrack = hwnd;
TrackMouseEvent(&tme);
window->m_mousePresent = true;
@ -1843,6 +1846,35 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
break;
}
case WM_MOUSEHOVER: {
/* Mouse Tracking is now off. TrackMouseEvent restarts in MouseMove. */
window->m_mousePresent = false;
/* Auto-focus only occurs within Blender windows, not with _other_ applications. */
HWND old_hwnd = ::GetFocus();
if (hwnd != old_hwnd) {
HWND new_parent = ::GetParent(hwnd);
HWND old_parent = ::GetParent(old_hwnd);
if (hwnd == old_parent || old_hwnd == new_parent) {
/* Child to its parent, parent to its child. */
::SetFocus(hwnd);
}
else if (new_parent != HWND_DESKTOP && new_parent == old_parent) {
/* Between siblings of same parent. */
::SetFocus(hwnd);
}
else if (!new_parent && !old_parent) {
/* Between main windows that don't overlap. */
RECT new_rect, old_rect, dest_rect;
::GetWindowRect(hwnd, &new_rect);
::GetWindowRect(old_hwnd, &old_rect);
if (!IntersectRect(&dest_rect, &new_rect, &old_rect)) {
::SetFocus(hwnd);
}
}
}
break;
}
case WM_MOUSEWHEEL: {
/* The WM_MOUSEWHEEL message is sent to the focus window
* when the mouse wheel is rotated. The DefWindowProc

View File

@ -1817,6 +1817,7 @@ def km_graph_editor(params):
("graph.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
("graph.paste", {"type": 'V', "value": 'PRESS', "shift": True, "ctrl": True},
{"properties": [("flipped", True)]}),
op_menu("GRAPH_MT_slider", {"type": 'D', "value": 'PRESS'}),
("graph.previewrange_set", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("graph.view_all", {"type": 'HOME', "value": 'PRESS'}, None),
("graph.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
@ -5636,6 +5637,8 @@ def km_curves(params):
("curves.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("curves.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("curves.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
*_template_items_proportional_editing(
params, connected=True, toggle_data_path='tool_settings.use_proportional_edit'),
])
return keymap

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later
from bpy.types import Panel
from bpy.app.translations import contexts as i18n_contexts
class ObjectConstraintPanel:

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Panel, Menu
from bpy.types import Panel, Menu, UIList
from rna_prop_ui import PropertyPanel
from bl_ui.properties_animviz import (
@ -91,6 +91,16 @@ class DATA_MT_bone_group_context_menu(Menu):
layout.operator("pose.group_sort", icon='SORTALPHA')
class DATA_UL_bone_groups(UIList):
def draw_item(self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index):
layout.prop(item, "name", text="", emboss=False, icon='GROUP_BONE')
if item.is_custom_color_set or item.color_set == 'DEFAULT':
layout.prop(item, "color_set", icon_only=True, icon="COLOR")
else:
layout.prop(item, "color_set", icon_only=True)
class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
bl_label = "Bone Groups"
bl_options = {'DEFAULT_CLOSED'}
@ -111,8 +121,9 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
rows = 1
if group:
rows = 4
row.template_list(
"UI_UL_list",
"DATA_UL_bone_groups",
"bone_groups",
pose,
"bone_groups",
@ -121,6 +132,7 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
rows=rows,
)
col = row.column(align=True)
col.operator("pose.group_add", icon='ADD', text="")
col.operator("pose.group_remove", icon='REMOVE', text="")
@ -130,17 +142,20 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
col.operator("pose.group_move", icon='TRIA_UP', text="").direction = 'UP'
col.operator("pose.group_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
split = layout.split()
if group.is_custom_color_set:
col = layout.column()
split = col.split(factor=0.4)
col = split.column()
col.prop(group, "color_set")
if group.color_set:
col = split.column()
sub = col.row(align=True)
sub.enabled = group.is_custom_color_set # only custom colors are editable
sub.prop(group.colors, "normal", text="")
sub.prop(group.colors, "select", text="")
sub.prop(group.colors, "active", text="")
row = col.row()
row.alignment = 'RIGHT'
row.label(text="Custom Colors")
col = split.column(align=True)
row = col.row(align=True)
row.prop(group.colors, "normal", text="")
row.prop(group.colors, "select", text="")
row.prop(group.colors, "active", text="")
row = layout.row()
@ -258,6 +273,7 @@ classes = (
DATA_PT_skeleton,
DATA_MT_bone_group_context_menu,
DATA_PT_bone_groups,
DATA_UL_bone_groups,
DATA_PT_motion_paths,
DATA_PT_motion_paths_display,
DATA_PT_display,

View File

@ -332,7 +332,7 @@ class GRAPH_MT_slider(Menu):
def draw(self, _context):
layout = self.layout
layout.operator_context = "INVOKE_DEFAULT"
layout.operator("graph.breakdown", text="Breakdown")
layout.operator("graph.blend_to_neighbor", text="Blend to Neighbor")
layout.operator("graph.blend_to_default", text="Blend to Default Value")

View File

@ -1067,10 +1067,10 @@ class VIEW3D_MT_transform(VIEW3D_MT_transform_base, Menu):
elif context.mode == 'EDIT_CURVE':
layout.operator("transform.transform", text="Radius").mode = 'CURVE_SHRINKFATTEN'
layout.separator()
layout.operator("transform.translate", text="Move Texture Space").texture_space = True
layout.operator("transform.resize", text="Scale Texture Space").texture_space = True
if context.mode != 'EDIT_CURVES':
layout.separator()
layout.operator("transform.translate", text="Move Texture Space").texture_space = True
layout.operator("transform.resize", text="Scale Texture Space").texture_space = True
# Object-specific extensions to Transform menu

View File

@ -1091,17 +1091,6 @@ static void blf_glyph_calc_rect(rcti *rect, GlyphBLF *g, const int x, const int
rect->ymax = rect->ymin - g->dims[1];
}
static void blf_glyph_calc_rect_test(rcti *rect, GlyphBLF *g, const int x, const int y)
{
/* Intentionally check with `g->advance`, because this is the
* width used by BLF_width. This allows that the text slightly
* overlaps the clipping border to achieve better alignment. */
rect->xmin = x;
rect->xmax = rect->xmin + MIN2(ft_pix_to_int(g->advance_x), g->dims[0]);
rect->ymin = y;
rect->ymax = rect->ymin - g->dims[1];
}
static void blf_glyph_calc_rect_shadow(
rcti *rect, GlyphBLF *g, const int x, const int y, FontBLF *font)
{
@ -1213,9 +1202,10 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x,
if (font->flags & BLF_CLIPPING) {
rcti rect_test;
blf_glyph_calc_rect_test(&rect_test, g, x, y);
BLI_rcti_translate(&rect_test, font->pos[0], font->pos[1]);
rect_test.xmin = x + font->pos[0] + g->pos[0] + 1;
rect_test.xmax = rect_test.xmin + g->dims[0] - 2;
rect_test.ymin = y + font->pos[1];
rect_test.ymax = rect_test.ymin + g->pos[1];
if (!BLI_rcti_inside_rcti(&font->clip_rec, &rect_test)) {
return;
}

View File

@ -1337,8 +1337,6 @@ static void layerCopyValue_propcol(const void *source,
memcpy(tmp_col, m1->color, sizeof(tmp_col));
}
blend_color_interpolate_float(m2->color, m2->color, tmp_col, mixfactor);
copy_v4_v4(m2->color, m1->color);
}
}

View File

@ -250,6 +250,88 @@ int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type)
/* ********** */
/**
* When transfering color attributes, also transfer the active color attribute string.
* If a match can't be found, use the first color layer that can be found (to ensure a valid string
* is set).
*/
static void data_transfer_mesh_attributes_transfer_active_color_string(
Mesh *mesh_dst, Mesh *mesh_src, const eAttrDomainMask mask_domain, const int data_type)
{
if (mesh_dst->active_color_attribute) {
return;
}
const char *active_color_src = BKE_id_attributes_active_color_name(&mesh_src->id);
if ((data_type == CD_PROP_COLOR) &&
!BKE_id_attribute_search(&mesh_src->id, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
return;
}
else if ((data_type == CD_PROP_BYTE_COLOR) &&
!BKE_id_attribute_search(&mesh_src->id, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
return;
}
if ((data_type == CD_PROP_COLOR) &&
BKE_id_attribute_search(&mesh_dst->id, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
mesh_dst->active_color_attribute = BLI_strdup(active_color_src);
}
else if ((data_type == CD_PROP_BYTE_COLOR) &&
BKE_id_attribute_search(&mesh_dst->id, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
mesh_dst->active_color_attribute = BLI_strdup(active_color_src);
}
else {
CustomDataLayer *first_color_layer = BKE_id_attribute_from_index(
&mesh_dst->id, 0, mask_domain, CD_MASK_COLOR_ALL);
if (first_color_layer != nullptr) {
mesh_dst->active_color_attribute = BLI_strdup(first_color_layer->name);
}
}
}
/**
* When transfering color attributes, also transfer the default color attribute string.
* If a match cant be found, use the first color layer that can be found (to ensure a valid string
* is set).
*/
static void data_transfer_mesh_attributes_transfer_default_color_string(
Mesh *mesh_dst, Mesh *mesh_src, const eAttrDomainMask mask_domain, const int data_type)
{
if (mesh_dst->default_color_attribute) {
return;
}
const char *default_color_src = BKE_id_attributes_default_color_name(&mesh_src->id);
if ((data_type == CD_PROP_COLOR) &&
!BKE_id_attribute_search(&mesh_src->id, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
return;
}
else if ((data_type == CD_PROP_BYTE_COLOR) &&
!BKE_id_attribute_search(&mesh_src->id, default_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
return;
}
if ((data_type == CD_PROP_COLOR) &&
BKE_id_attribute_search(&mesh_dst->id, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
mesh_dst->default_color_attribute = BLI_strdup(default_color_src);
}
else if ((data_type == CD_PROP_BYTE_COLOR) &&
BKE_id_attribute_search(&mesh_dst->id, default_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) {
mesh_dst->default_color_attribute = BLI_strdup(default_color_src);
}
else {
CustomDataLayer *first_color_layer = BKE_id_attribute_from_index(
&mesh_dst->id, 0, mask_domain, CD_MASK_COLOR_ALL);
if (first_color_layer != nullptr) {
mesh_dst->default_color_attribute = BLI_strdup(first_color_layer->name);
}
}
}
/* ********** */
/* Generic pre/post processing, only used by custom loop normals currently. */
static void data_transfer_dtdata_type_preprocess(Mesh *me_src,
@ -1124,6 +1206,14 @@ void BKE_object_data_transfer_layout(struct Depsgraph *depsgraph,
fromlayers,
tolayers,
nullptr);
/* Make sure we have active/defaut color layers if none existed before.
* Use the active/defaut from src (if it was transferred), otherwise the first. */
if (ELEM(cddata_type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) {
data_transfer_mesh_attributes_transfer_active_color_string(
me_dst, me_src, ATTR_DOMAIN_MASK_POINT, cddata_type);
data_transfer_mesh_attributes_transfer_default_color_string(
me_dst, me_src, ATTR_DOMAIN_MASK_POINT, cddata_type);
}
}
if (DT_DATATYPE_IS_EDGE(dtdata_type)) {
const int num_elem_dst = me_dst->totedge;
@ -1164,6 +1254,14 @@ void BKE_object_data_transfer_layout(struct Depsgraph *depsgraph,
fromlayers,
tolayers,
nullptr);
/* Make sure we have active/defaut color layers if none existed before.
* Use the active/defaut from src (if it was transferred), otherwise the first. */
if (ELEM(cddata_type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) {
data_transfer_mesh_attributes_transfer_active_color_string(
me_dst, me_src, ATTR_DOMAIN_MASK_CORNER, cddata_type);
data_transfer_mesh_attributes_transfer_default_color_string(
me_dst, me_src, ATTR_DOMAIN_MASK_CORNER, cddata_type);
}
}
if (DT_DATATYPE_IS_POLY(dtdata_type)) {
const int num_elem_dst = me_dst->totpoly;

View File

@ -2719,6 +2719,11 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
if (attr) {
sculpt_attr_update(ob, attr);
/* Since "stroke_only" is not a CustomData flag we have
* to sync its parameter setting manually. Fixes #104618.
*/
attr->params.stroke_only = params->stroke_only;
return attr;
}

View File

@ -91,8 +91,6 @@ struct SampleSegmentHint {
* \param sample_length: The position to sample at.
* \param r_segment_index: Returns the index of the segment that #sample_length is in.
* \param r_factor: Returns the position within the segment.
*
* \note #sample_length must not be outside of any segment.
*/
inline void sample_at_length(const Span<float> accumulated_segment_lengths,
const float sample_length,
@ -105,7 +103,6 @@ inline void sample_at_length(const Span<float> accumulated_segment_lengths,
BLI_assert(lengths.size() > 0);
BLI_assert(sample_length >= 0.0f);
BLI_assert(sample_length <= lengths.last() + 0.00001f);
if (hint != nullptr && hint->segment_index >= 0) {
const float length_in_segment = sample_length - hint->segment_start;

View File

@ -102,7 +102,7 @@ Closure closure_eval(ClosureTranslucent translucent)
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(GlossyBSDF, Glossy)
Closure closure_eval(ClosureReflection reflection)
Closure closure_eval(ClosureReflection reflection, const bool do_output_ssr)
{
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_1(Glossy);
@ -113,12 +113,22 @@ Closure closure_eval(ClosureReflection reflection)
CLOSURE_EVAL_FUNCTION_1(GlossyBSDF, Glossy);
Closure closure = CLOSURE_DEFAULT;
if (!output_ssr(reflection)) {
bool output_radiance = true;
if (do_output_ssr) {
output_radiance = !output_ssr(reflection);
}
if (output_radiance) {
closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
}
return closure;
}
Closure closure_eval(ClosureReflection reflection)
{
return closure_eval(reflection, true);
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(RefractionBSDF, Refraction)
Closure closure_eval(ClosureRefraction refraction)
{
@ -155,6 +165,13 @@ Closure closure_eval(ClosureTransparency transparency)
CLOSURE_EVAL_FUNCTION_DECLARE_2(GlassBSDF, Glossy, Refraction)
Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(refraction);
Closure closure_reflection = closure_eval(reflection);
closure.radiance += closure_reflection.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_2(Glossy, Refraction);
@ -172,12 +189,19 @@ Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Dielectric BSDF */
CLOSURE_EVAL_FUNCTION_DECLARE_2(DielectricBSDF, Diffuse, Glossy)
Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(diffuse);
Closure closure_reflection = closure_eval(reflection);
closure.radiance += closure_reflection.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_2(Diffuse, Glossy);
@ -198,6 +222,7 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Specular BSDF */
@ -206,6 +231,13 @@ Closure closure_eval(ClosureDiffuse diffuse,
ClosureReflection reflection,
ClosureReflection clearcoat)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(diffuse);
Closure closure_reflection = closure_eval(reflection);
Closure closure_clearcoat = closure_eval(clearcoat, false);
closure.radiance += closure_reflection.radiance + closure_clearcoat.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
@ -229,6 +261,7 @@ Closure closure_eval(ClosureDiffuse diffuse,
closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Principled BSDF */
@ -238,6 +271,15 @@ Closure closure_eval(ClosureDiffuse diffuse,
ClosureReflection clearcoat,
ClosureRefraction refraction)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(diffuse);
Closure closure_reflection = closure_eval(reflection);
Closure closure_clearcoat = closure_eval(clearcoat, false);
Closure closure_refraction = closure_eval(refraction);
closure.radiance += closure_reflection.radiance + closure_clearcoat.radiance +
closure_refraction.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
@ -263,11 +305,18 @@ Closure closure_eval(ClosureDiffuse diffuse,
closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
CLOSURE_EVAL_FUNCTION_DECLARE_2(PrincipledBSDFMetalClearCoat, Glossy, Glossy)
Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(clearcoat);
Closure closure_reflection = closure_eval(reflection);
closure.radiance += closure_reflection.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_2(Glossy, Glossy);
@ -284,6 +333,7 @@ Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat)
closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Not supported for surface shaders. */

View File

@ -16,6 +16,14 @@ typedef struct CommonUniformBlock CommonUniformBlock;
# endif
#endif
/* NOTE: AMD-based macOS platforms experience performance and correctness issues with EEVEE
* material closure evaluation. Using singular closure evaluation, rather than the compound
* function calls reduces register overflow, by limiting the simultaneous number of live
* registers used by the virtual GPU function stack. */
#if (defined(GPU_METAL) && defined(GPU_ATI))
# define DO_SPLIT_CLOSURE_EVAL 1
#endif
struct CommonUniformBlock {
mat4 pastViewProjectionMatrix;
vec4 hizUvScale; /* To correct mip level texel misalignment */

View File

@ -20,12 +20,14 @@
#include "DNA_anim_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "BKE_action.h"
#include "BKE_curve.h"
#include "BKE_fcurve.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "RNA_access.h"
#include "RNA_enum_types.h"
@ -1074,7 +1076,7 @@ static void do_curve_mirror_flippping(tAnimCopybufItem *aci, BezTriple *bezt)
/* helper for paste_animedit_keys() - performs the actual pasting */
static void paste_animedit_keys_fcurve(
FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode, bool flip)
FCurve *fcu, tAnimCopybufItem *aci, float offset[2], const eKeyMergeMode merge_mode, bool flip)
{
BezTriple *bezt;
int i;
@ -1101,12 +1103,12 @@ static void paste_animedit_keys_fcurve(
float f_max;
if (merge_mode == KEYFRAME_PASTE_MERGE_OVER_RANGE) {
f_min = aci->bezt[0].vec[1][0] + offset;
f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset;
f_min = aci->bezt[0].vec[1][0] + offset[0];
f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset[0];
}
else { /* Entire Range */
f_min = animcopy_firstframe + offset;
f_max = animcopy_lastframe + offset;
f_min = animcopy_firstframe + offset[0];
f_max = animcopy_lastframe + offset[0];
}
/* remove keys in range */
@ -1132,9 +1134,9 @@ static void paste_animedit_keys_fcurve(
do_curve_mirror_flippping(aci, bezt);
}
bezt->vec[0][0] += offset;
bezt->vec[1][0] += offset;
bezt->vec[2][0] += offset;
add_v2_v2(bezt->vec[0], offset);
add_v2_v2(bezt->vec[1], offset);
add_v2_v2(bezt->vec[2], offset);
/* insert the keyframe
* NOTE: we do not want to inherit handles from existing keyframes in this case!
@ -1143,9 +1145,9 @@ static void paste_animedit_keys_fcurve(
insert_bezt_fcurve(fcu, bezt, INSERTKEY_OVERWRITE_FULL);
/* un-apply offset from src beztriple after copying */
bezt->vec[0][0] -= offset;
bezt->vec[1][0] -= offset;
bezt->vec[2][0] -= offset;
sub_v2_v2(bezt->vec[0], offset);
sub_v2_v2(bezt->vec[1], offset);
sub_v2_v2(bezt->vec[2], offset);
if (flip) {
do_curve_mirror_flippping(aci, bezt);
@ -1172,6 +1174,34 @@ const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = {
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_keyframe_paste_offset_value[] = {
{KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY,
"LEFT_KEY",
0,
"Left Key",
"Paste keys with the first key matching the key left of the cursor"},
{KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY,
"RIGHT_KEY",
0,
"Right Key",
"Paste keys with the last key matching the key right of the cursor"},
{KEYFRAME_PASTE_VALUE_OFFSET_CFRA,
"CURRENT_FRAME",
0,
"Current Frame Value",
"Paste keys relative to the value of the curve under the cursor"},
{KEYFRAME_PASTE_VALUE_OFFSET_CURSOR,
"CURSOR_VALUE",
0,
"Cursor Value",
"Paste keys relative to the Y-Position of the cursor"},
{KEYFRAME_PASTE_VALUE_OFFSET_NONE,
"NONE",
0,
"No Offset",
"Paste keys with the same value as they were copied"},
};
const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = {
{KEYFRAME_PASTE_MERGE_MIX, "MIX", 0, "Mix", "Overlay existing with new keys"},
{KEYFRAME_PASTE_MERGE_OVER, "OVER_ALL", 0, "Overwrite All", "Replace all keys"},
@ -1188,9 +1218,56 @@ const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = {
{0, NULL, 0, NULL, NULL},
};
static float paste_get_y_offset(bAnimContext *ac,
tAnimCopybufItem *aci,
bAnimListElem *ale,
const eKeyPasteValueOffset value_offset_mode)
{
FCurve *fcu = (FCurve *)ale->data;
const float cfra = BKE_scene_frame_get(ac->scene);
switch (value_offset_mode) {
case KEYFRAME_PASTE_VALUE_OFFSET_CURSOR: {
SpaceGraph *sipo = (SpaceGraph *)ac->sl;
const float offset = sipo->cursorVal - aci->bezt[0].vec[1][1];
return offset;
}
case KEYFRAME_PASTE_VALUE_OFFSET_CFRA: {
const float cfra_y = evaluate_fcurve(fcu, cfra);
const float offset = cfra_y - aci->bezt[0].vec[1][1];
return offset;
}
case KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY: {
bool replace;
const int fcu_index = BKE_fcurve_bezt_binarysearch_index(
fcu->bezt, cfra, fcu->totvert, &replace);
BezTriple left_key = fcu->bezt[max_ii(fcu_index - 1, 0)];
const float offset = left_key.vec[1][1] - aci->bezt[0].vec[1][1];
return offset;
}
case KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY: {
bool replace;
const int fcu_index = BKE_fcurve_bezt_binarysearch_index(
fcu->bezt, cfra, fcu->totvert, &replace);
BezTriple right_key = fcu->bezt[min_ii(fcu_index, fcu->totvert - 1)];
const float offset = right_key.vec[1][1] - aci->bezt[aci->totvert - 1].vec[1][1];
return offset;
}
case KEYFRAME_PASTE_VALUE_OFFSET_NONE:
break;
}
return 0.0f;
}
eKeyPasteError paste_animedit_keys(bAnimContext *ac,
ListBase *anim_data,
const eKeyPasteOffset offset_mode,
const eKeyPasteValueOffset value_offset_mode,
const eKeyMergeMode merge_mode,
bool flip)
{
@ -1201,7 +1278,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac,
const bool from_single = BLI_listbase_is_single(&animcopybuf);
const bool to_simple = BLI_listbase_is_single(anim_data);
float offset = 0.0f;
float offset[2];
int pass;
/* check if buffer is empty */
@ -1216,16 +1293,16 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac,
/* methods of offset */
switch (offset_mode) {
case KEYFRAME_PASTE_OFFSET_CFRA_START:
offset = (float)(scene->r.cfra - animcopy_firstframe);
offset[0] = (float)(scene->r.cfra - animcopy_firstframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_END:
offset = (float)(scene->r.cfra - animcopy_lastframe);
offset[0] = (float)(scene->r.cfra - animcopy_lastframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
offset = (float)(scene->r.cfra - animcopy_cfra);
offset[0] = (float)(scene->r.cfra - animcopy_cfra);
break;
case KEYFRAME_PASTE_OFFSET_NONE:
offset = 0.0f;
offset[0] = 0.0f;
break;
}
@ -1238,6 +1315,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac,
fcu = (FCurve *)ale->data; /* destination F-Curve */
aci = animcopybuf.first;
offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode);
paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, false);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@ -1282,6 +1360,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac,
if (aci) {
totmatch++;
offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode);
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, flip);

View File

@ -218,6 +218,19 @@ typedef enum eKeyPasteOffset {
KEYFRAME_PASTE_OFFSET_NONE,
} eKeyPasteOffset;
typedef enum eKeyPasteValueOffset {
/* Paste keys with the first key matching the key left of the cursor. */
KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY,
/* Paste keys with the last key matching the key right of the cursor. */
KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY,
/* Paste keys relative to the value of the curve under the cursor. */
KEYFRAME_PASTE_VALUE_OFFSET_CFRA,
/* Paste values relative to the cursor position. */
KEYFRAME_PASTE_VALUE_OFFSET_CURSOR,
/* Paste keys with the exact copied value. */
KEYFRAME_PASTE_VALUE_OFFSET_NONE,
} eKeyPasteValueOffset;
typedef enum eKeyMergeMode {
/* overlay existing with new keys */
KEYFRAME_PASTE_MERGE_MIX,
@ -427,6 +440,7 @@ short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data);
eKeyPasteError paste_animedit_keys(struct bAnimContext *ac,
ListBase *anim_data,
eKeyPasteOffset offset_mode,
eKeyPasteValueOffset value_offset_mode,
eKeyMergeMode merge_mode,
bool flip);

View File

@ -6532,6 +6532,8 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
va_list args;
uiStringInfo *si;
PointerRNA *opptr = UI_but_operator_ptr_get(but);
const EnumPropertyItem *items = nullptr, *item = nullptr;
int totitems;
bool free_items = false;
@ -6610,10 +6612,13 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
}
else if (but->optype) {
if (type == BUT_GET_RNA_LABEL) {
tmp = BLI_strdup(WM_operatortype_name(but->optype, but->opptr));
tmp = BLI_strdup(WM_operatortype_name(but->optype, opptr));
}
else {
tmp = WM_operatortype_description(C, but->optype, but->opptr);
bContextStore *previous_ctx = CTX_store_get(C);
CTX_store_set(C, but->context);
tmp = WM_operatortype_description(C, but->optype, opptr);
CTX_store_set(C, previous_ctx);
}
}
else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) {
@ -6696,7 +6701,6 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
int(ui_but_value_get(but));
}
else if (but->optype) {
PointerRNA *opptr = UI_but_operator_ptr_get(but);
wmOperatorType *ot = but->optype;
/* So the context is passed to `itemf` functions. */

View File

@ -212,106 +212,142 @@ void paintface_reveal(bContext *C, Object *ob, const bool select)
paintface_flush_flags(C, ob, true, true);
}
/* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */
static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select)
/**
* Join all edges of each poly in the AtomicDisjointSet. This can be used to find out which polys
* are connected to each other.
* \param islands Is expected to be of length mesh->totedge.
* \param skip_seams Polys separated by a seam will be treated as not connected.
*/
static void build_poly_connections(blender::AtomicDisjointSet &islands,
Mesh &mesh,
const bool skip_seams = true)
{
using namespace blender;
bool do_it = true;
bool mark = false;
const Span<MPoly> polys = mesh.polys();
const Span<MEdge> edges = mesh.edges();
const Span<MLoop> loops = mesh.loops();
BLI_bitmap *edge_tag = BLI_BITMAP_NEW(me->totedge, __func__);
BLI_bitmap *poly_tag = BLI_BITMAP_NEW(me->totpoly, __func__);
const Span<MEdge> edges = me->edges();
const Span<MPoly> polys = me->polys();
const Span<MLoop> loops = me->loops();
bke::MutableAttributeAccessor attributes = me->attributes_for_write();
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
".hide_poly", ATTR_DOMAIN_FACE, false);
/* Polys are connected if they share edges. By connecting all edges of a loop (as long as they
* are not a seam) we can find connected faces. */
threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
for (const int poly_index : range) {
if (hide_poly[poly_index]) {
continue;
}
const MPoly &poly = polys[poly_index];
const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
for (const int poly_loop_index : poly_loops.index_range()) {
const MLoop &outer_mloop = poly_loops[poly_loop_index];
if (skip_seams && (edges[outer_mloop.e].flag & ME_SEAM) != 0) {
continue;
}
for (const MLoop &inner_mloop :
poly_loops.slice(poly_loop_index, poly_loops.size() - poly_loop_index)) {
if (&outer_mloop == &inner_mloop) {
continue;
}
if (skip_seams && (edges[inner_mloop.e].flag & ME_SEAM) != 0) {
continue;
}
islands.join(inner_mloop.e, outer_mloop.e);
}
}
}
});
}
/* Select faces connected to the given face_indices. Seams are treated as separation. */
static void paintface_select_linked_faces(Mesh &mesh,
const blender::Span<int> face_indices,
const bool select)
{
using namespace blender;
AtomicDisjointSet islands(mesh.totedge);
build_poly_connections(islands, mesh);
const Span<MPoly> polys = mesh.polys();
const Span<MEdge> edges = mesh.edges();
const Span<MLoop> loops = mesh.loops();
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
".select_poly", ATTR_DOMAIN_FACE);
if (index != uint(-1)) {
/* only put face under cursor in array */
const MPoly &poly = polys[index];
BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]);
BLI_BITMAP_ENABLE(poly_tag, index);
}
else {
/* fill array by selection */
for (int i = 0; i < me->totpoly; i++) {
if (hide_poly[i]) {
/* pass */
}
else if (select_poly.span[i]) {
const MPoly &poly = polys[i];
BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]);
BLI_BITMAP_ENABLE(poly_tag, i);
}
}
}
while (do_it) {
do_it = false;
/* expand selection */
for (int i = 0; i < me->totpoly; i++) {
if (hide_poly[i]) {
Set<int> selected_roots;
for (const int i : face_indices) {
const MPoly &poly = polys[i];
for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
if ((edges[loop.e].flag & ME_SEAM) != 0) {
continue;
}
const int root = islands.find_root(loop.e);
selected_roots.add(root);
}
}
if (!BLI_BITMAP_TEST(poly_tag, i)) {
mark = false;
const MPoly &poly = polys[i];
const MLoop *ml = &loops[poly.loopstart];
for (int b = 0; b < poly.totloop; b++, ml++) {
if ((edges[ml->e].flag & ME_SEAM) == 0) {
if (BLI_BITMAP_TEST(edge_tag, ml->e)) {
mark = true;
break;
}
}
}
if (mark) {
BLI_BITMAP_ENABLE(poly_tag, i);
BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]);
do_it = true;
threading::parallel_for(select_poly.span.index_range(), 1024, [&](const IndexRange range) {
for (const int poly_index : range) {
const MPoly &poly = polys[poly_index];
for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
const int root = islands.find_root(loop.e);
if (selected_roots.contains(root)) {
select_poly.span[poly_index] = select;
break;
}
}
}
}
});
MEM_freeN(edge_tag);
for (int i = 0; i < me->totpoly; i++) {
if (BLI_BITMAP_TEST(poly_tag, i)) {
select_poly.span[i] = select;
}
}
MEM_freeN(poly_tag);
select_poly.finish();
}
void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select)
{
uint index = uint(-1);
using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr || me->totpoly == 0) {
return;
}
bke::MutableAttributeAccessor attributes = me->attributes_for_write();
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
".select_poly", ATTR_DOMAIN_FACE);
Vector<int> indices;
if (mval) {
uint index = uint(-1);
if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
select_poly.finish();
return;
}
/* Since paintface_select_linked_faces might not select the face under the cursor, select it
* here. */
select_poly.span[index] = true;
indices.append(index);
}
else {
for (const int i : select_poly.span.index_range()) {
if (!select_poly.span[i]) {
continue;
}
indices.append(i);
}
}
select_linked_tfaces_with_seams(me, index, select);
select_poly.finish();
paintface_select_linked_faces(*me, indices, select);
paintface_flush_flags(C, ob, true, false);
}

View File

@ -970,9 +970,9 @@ static void min_distance_edit_draw(bContext *C, int /*x*/, int /*y*/, void *cust
const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
const uint siz3d = GPU_vertformat_attr_add(format3d, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
immUniform1f("size", 4.0f);
immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR);
immBegin(GPU_PRIM_POINTS, points_wo.size());
float3 brush_origin_wo = math::transform_point(op_data.curves_to_world_mat, op_data.pos_cu);
@ -990,6 +990,7 @@ static void min_distance_edit_draw(bContext *C, int /*x*/, int /*y*/, void *cust
const float dist_to_point_re = math::distance(pos_re, brush_origin_re);
const float alpha = 1.0f - ((dist_to_point_re - dist_to_inner_border_re) / alpha_border_re);
immAttr1f(siz3d, 3.0f);
immAttr4f(col3d, 0.9f, 0.9f, 0.9f, alpha);
immVertex3fv(pos3d, pos_wo);
}

View File

@ -81,6 +81,19 @@ using blender::MutableSpan;
using blender::Set;
using blender::Vector;
static float sculpt_calc_radius(ViewContext *vc,
const Brush *brush,
const Scene *scene,
const float3 location)
{
if (!BKE_brush_use_locked_size(scene, brush)) {
return paint_calc_object_space_radius(vc, location, BKE_brush_size_get(scene, brush));
}
else {
return BKE_brush_unprojected_radius_get(scene, brush);
}
}
/* -------------------------------------------------------------------- */
/** \name Sculpt PBVH Abstraction API
*
@ -4925,14 +4938,11 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
/* Truly temporary data that isn't stored in properties. */
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
cache->initial_radius = sculpt_calc_radius(cache->vc, brush, scene, cache->true_location);
if (!BKE_brush_use_locked_size(scene, brush)) {
cache->initial_radius = paint_calc_object_space_radius(
cache->vc, cache->true_location, BKE_brush_size_get(scene, brush));
BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius);
}
else {
cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush);
}
}
/* Clay stabilized pressure. */
@ -5280,6 +5290,19 @@ bool SCULPT_stroke_get_location(bContext *C,
float out[3],
const float mval[2],
bool force_original)
{
const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
bool check_closest = brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE;
return SCULPT_stroke_get_location_ex(C, out, mval, force_original, check_closest, true);
}
bool SCULPT_stroke_get_location_ex(bContext *C,
float out[3],
const float mval[2],
bool force_original,
bool check_closest,
bool limit_closest_radius)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob;
@ -5329,11 +5352,7 @@ bool SCULPT_stroke_get_location(bContext *C,
}
}
if (hit) {
return hit;
}
if (!ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) {
if (hit || !check_closest) {
return hit;
}
@ -5348,14 +5367,20 @@ bool SCULPT_stroke_get_location(bContext *C,
BKE_pbvh_find_nearest_to_ray(
ss->pbvh, sculpt_find_nearest_to_ray_cb, &srd, ray_start, ray_normal, srd.original);
if (srd.hit) {
if (srd.hit && srd.dist_sq_to_ray) {
hit = true;
copy_v3_v3(out, ray_normal);
mul_v3_fl(out, srd.depth);
add_v3_v3(out, ray_start);
}
return hit;
float closest_radius_sq = FLT_MAX;
if (limit_closest_radius) {
closest_radius_sq = sculpt_calc_radius(&vc, brush, CTX_data_scene(C), out);
closest_radius_sq *= closest_radius_sq;
}
return hit && srd.dist_sq_to_ray < closest_radius_sq;
}
static void sculpt_brush_init_tex(Sculpt *sd, SculptSession *ss)
@ -5620,7 +5645,12 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
static bool over_mesh(bContext *C, wmOperator * /*op*/, const float mval[2])
{
float co_dummy[3];
return SCULPT_stroke_get_location(C, co_dummy, mval, false);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
bool check_closest = brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE;
return SCULPT_stroke_get_location_ex(C, co_dummy, mval, false, check_closest, true);
}
static void sculpt_stroke_undo_begin(const bContext *C, wmOperator *op)
@ -5974,29 +6004,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
bool started = op->customdata && paint_stroke_started((PaintStroke *)op->customdata);
int retval = paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata);
if (!started && ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
/* Did the stroke never start? If so push a blank sculpt undo
* step to prevent a global undo step (which is triggered by the
* #OPTYPE_UNDO flag in #SCULPT_OT_brush_stroke).
*
* Having blank global undo steps interleaved with sculpt steps
* corrupts the DynTopo undo stack.
* See #101430.
*
* NOTE: simply returning #OPERATOR_CANCELLED was not
* sufficient to prevent this. */
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
sculpt_stroke_undo_begin(C, op);
sculpt_stroke_undo_end(C, brush);
}
return retval;
return paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata);
}
static void sculpt_redo_empty_ui(bContext * /*C*/, wmOperator * /*op*/)

View File

@ -24,6 +24,8 @@
#include "ED_view3d.h"
#include <functional>
struct AutomaskingCache;
struct AutomaskingNodeData;
struct Dial;
@ -892,7 +894,18 @@ void SCULPT_tag_update_overlays(bContext *C);
* Do a ray-cast in the tree to find the 3d brush location
* (This allows us to ignore the GL depth buffer)
* Returns 0 if the ray doesn't hit the mesh, non-zero otherwise.
*
* If check_closest is true and the ray test fails a point closest
* to the ray will be found. If limit_closest_radius is true then
* the closest point will be tested against the active brush radius.
*/
bool SCULPT_stroke_get_location_ex(bContext *C,
float out[3],
const float mval[2],
bool force_original,
bool check_closest,
bool limit_closest_radius);
bool SCULPT_stroke_get_location(bContext *C,
float out[3],
const float mouse[2],

View File

@ -533,8 +533,9 @@ static eKeyPasteError paste_action_keys(bAnimContext *ac,
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
}
/* paste keyframes */
const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip);
/* Value offset is always None because the user cannot see the effect of it. */
const eKeyPasteError ok = paste_animedit_keys(
ac, &anim_data, offset_mode, KEYFRAME_PASTE_VALUE_OFFSET_NONE, merge_mode, flip);
/* clean up */
ANIM_animdata_freelist(&anim_data);

View File

@ -942,6 +942,7 @@ static void graph_panel_drivers_header(const bContext *C, Panel *panel)
}
graph_draw_driven_property_enabled_btn(panel->layout, ale->id, fcu, IFACE_("Driver"));
MEM_freeN(ale);
}
static void graph_draw_driven_property_panel(uiLayout *layout, ID *id, FCurve *fcu)

View File

@ -475,6 +475,7 @@ static short copy_graph_keys(bAnimContext *ac)
static eKeyPasteError paste_graph_keys(bAnimContext *ac,
const eKeyPasteOffset offset_mode,
const eKeyPasteValueOffset value_offset_mode,
const eKeyMergeMode merge_mode,
bool flip)
{
@ -495,7 +496,8 @@ static eKeyPasteError paste_graph_keys(bAnimContext *ac,
}
/* Paste keyframes. */
const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip);
const eKeyPasteError ok = paste_animedit_keys(
ac, &anim_data, offset_mode, value_offset_mode, merge_mode, flip);
/* Clean up. */
ANIM_animdata_freelist(&anim_data);
@ -544,6 +546,7 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op)
bAnimContext ac;
const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset");
const eKeyPasteValueOffset value_offset_mode = RNA_enum_get(op->ptr, "value_offset");
const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge");
const bool flipped = RNA_boolean_get(op->ptr, "flipped");
@ -555,7 +558,8 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op)
/* Ac.reports by default will be the global reports list, which won't show warnings. */
ac.reports = op->reports;
const eKeyPasteError kf_empty = paste_graph_keys(&ac, offset_mode, merge_mode, flipped);
const eKeyPasteError kf_empty = paste_graph_keys(
&ac, offset_mode, value_offset_mode, merge_mode, flipped);
switch (kf_empty) {
case KEYFRAME_PASTE_OK:
break;
@ -614,8 +618,14 @@ void GRAPH_OT_paste(wmOperatorType *ot)
"offset",
rna_enum_keyframe_paste_offset_items,
KEYFRAME_PASTE_OFFSET_CFRA_START,
"Offset",
"Frame Offset",
"Paste time offset of keys");
RNA_def_enum(ot->srna,
"value_offset",
rna_enum_keyframe_paste_offset_value,
KEYFRAME_PASTE_VALUE_OFFSET_NONE,
"Value Offset",
"Paste keys with a value offset");
RNA_def_enum(ot->srna,
"merge",
rna_enum_keyframe_paste_merge_items,

View File

@ -456,6 +456,22 @@ static int node_add_group_asset_invoke(bContext *C, wmOperator *op, const wmEven
return OPERATOR_FINISHED;
}
static char *node_add_group_asset_get_description(struct bContext *C,
struct wmOperatorType * /*op*/,
struct PointerRNA * /*values*/)
{
bool is_valid;
const AssetHandle handle = CTX_wm_asset_handle(C, &is_valid);
if (!is_valid) {
return nullptr;
}
const AssetMetaData &asset_data = *ED_asset_handle_get_metadata(&handle);
if (!asset_data.description) {
return nullptr;
}
return BLI_strdup(asset_data.description);
}
void NODE_OT_add_group_asset(wmOperatorType *ot)
{
ot->name = "Add Node Group Asset";
@ -464,6 +480,7 @@ void NODE_OT_add_group_asset(wmOperatorType *ot)
ot->invoke = node_add_group_asset_invoke;
ot->poll = node_add_group_poll;
ot->get_description = node_add_group_asset_get_description;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}

View File

@ -1106,6 +1106,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d)
/* -- draw rotation center -- */
immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
immUniform1f("size", 7.0f);
immUniform4fv("color", float4(color));
immBegin(GPU_PRIM_POINTS, 1);
immAttr4ubv(col, color);
immVertex3fv(pos, o);

View File

@ -108,15 +108,12 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t)
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
const bool has_any_selected = ed::curves::has_anything_selected(selection, points);
if (!has_any_selected) {
if (!has_any_selected && use_connected_only) {
for (const int point_i : points) {
TransData &td = tc.data[point_i];
td.flag |= TD_NOTCONNECTED;
td.dist = FLT_MAX;
}
if (use_connected_only) {
continue;
td.flag |= TD_SKIP;
}
continue;
}
closest_distances.reinitialize(points.size());

View File

@ -154,6 +154,12 @@ struct RealizeCurveInfo {
* curves.
*/
VArray<int> resolution;
/**
* The resolution attribute must be filled with the default value if it does not exist on some
* curves.
*/
Span<float> nurbs_weight;
};
/** Start indices in the final output curves data-block. */
@ -208,6 +214,7 @@ struct AllCurvesInfo {
bool create_handle_postion_attributes = false;
bool create_radius_attribute = false;
bool create_resolution_attribute = false;
bool create_nurbs_weight_attribute = false;
};
/** Collects all tasks that need to be executed to realize all instances. */
@ -1159,6 +1166,7 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate(
attributes_to_propagate);
attributes_to_propagate.remove("position");
attributes_to_propagate.remove("radius");
attributes_to_propagate.remove("nurbs_weight");
attributes_to_propagate.remove("resolution");
attributes_to_propagate.remove("handle_right");
attributes_to_propagate.remove("handle_left");
@ -1220,20 +1228,20 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set,
}
}
/* Retrieve the radius attribute, if it exists. */
if (attributes.contains("radius")) {
curve_info.radius =
attributes.lookup<float>("radius", ATTR_DOMAIN_POINT).get_internal_span();
info.create_radius_attribute = true;
}
/* Retrieve the resolution attribute, if it exists. */
if (attributes.contains("nurbs_weight")) {
curve_info.nurbs_weight =
attributes.lookup<float>("nurbs_weight", ATTR_DOMAIN_POINT).get_internal_span();
info.create_nurbs_weight_attribute = true;
}
curve_info.resolution = curves.resolution();
if (attributes.contains("resolution")) {
info.create_resolution_attribute = true;
}
/* Retrieve handle position attributes, if they exist. */
if (attributes.contains("handle_right")) {
curve_info.handle_left =
attributes.lookup<float3>("handle_left", ATTR_DOMAIN_POINT).get_internal_span();
@ -1255,6 +1263,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
MutableSpan<float3> all_handle_left,
MutableSpan<float3> all_handle_right,
MutableSpan<float> all_radii,
MutableSpan<float> all_nurbs_weights,
MutableSpan<int> all_resolutions)
{
const RealizeCurveInfo &curves_info = *task.curve_info;
@ -1285,14 +1294,20 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
}
}
/* Copy radius attribute with 1.0 default if it doesn't exist. */
auto copy_point_span_with_default =
[&](const Span<float> src, MutableSpan<float> all_dst, const float value) {
if (src.is_empty()) {
all_dst.slice(dst_point_range).fill(value);
}
else {
all_dst.slice(dst_point_range).copy_from(src);
}
};
if (all_curves_info.create_radius_attribute) {
if (curves_info.radius.is_empty()) {
all_radii.slice(dst_point_range).fill(1.0f);
}
else {
all_radii.slice(dst_point_range).copy_from(curves_info.radius);
}
copy_point_span_with_default(curves_info.radius, all_radii, 1.0f);
}
if (all_curves_info.create_nurbs_weight_attribute) {
copy_point_span_with_default(curves_info.nurbs_weight, all_nurbs_weights, 1.0f);
}
if (all_curves_info.create_resolution_attribute) {
@ -1385,13 +1400,15 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
ATTR_DOMAIN_POINT);
}
/* Prepare radius attribute if necessary. */
SpanAttributeWriter<float> radius;
if (all_curves_info.create_radius_attribute) {
radius = dst_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
}
/* Prepare resolution attribute if necessary. */
SpanAttributeWriter<float> nurbs_weight;
if (all_curves_info.create_nurbs_weight_attribute) {
nurbs_weight = dst_attributes.lookup_or_add_for_write_only_span<float>("nurbs_weight",
ATTR_DOMAIN_POINT);
}
SpanAttributeWriter<int> resolution;
if (all_curves_info.create_resolution_attribute) {
resolution = dst_attributes.lookup_or_add_for_write_only_span<int>("resolution",
@ -1412,6 +1429,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
handle_left.span,
handle_right.span,
radius.span,
nurbs_weight.span,
resolution.span);
}
});
@ -1432,6 +1450,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
point_ids.finish();
radius.finish();
resolution.finish();
nurbs_weight.finish();
handle_left.finish();
handle_right.finish();
}

View File

@ -308,24 +308,23 @@ static bke::CurvesGeometry convert_curves_to_bezier(
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
MutableSpan<float> dst_weights = dst_curves.nurbs_weights_for_write();
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
Set<std::string> attributes_to_skip = {
"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"};
if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
attributes_to_skip.add_new("nurbs_weight");
}
Vector<bke::AttributeTransferData> generic_attributes = bke::retrieve_attributes_for_transfer(
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_type_left",
"handle_type_right",
"handle_right",
"handle_left",
"nurbs_weight"});
attributes_to_skip);
auto catmull_rom_to_bezier = [&](IndexMask selection) {
bke::curves::fill_points<int8_t>(
@ -396,7 +395,6 @@ static bke::CurvesGeometry convert_curves_to_bezier(
dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
bke::curves::fill_points<int8_t>(
dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
bke::curves::fill_points<float>(dst_points_by_curve, selection, 0.0f, dst_weights);
threading::parallel_for(selection.index_range(), 64, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
@ -513,7 +511,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
"nurbs_weight"});
auto fill_weights_if_necessary = [&](const IndexMask selection) {
if (!src_curves.nurbs_weights().is_empty()) {
if (src_attributes.contains("nurbs_weight")) {
bke::curves::fill_points(
dst_points_by_curve, selection, 1.0f, dst_curves.nurbs_weights_for_write());
}

View File

@ -951,14 +951,16 @@ void GPU_material_compile(GPUMaterial *mat)
* As PSOs do not always match for default shaders, we limit warming for PSO
* configurations to ensure compile time remains fast, as these first
* entries will be the most commonly used PSOs. As not all PSOs are necessarily
* required immediately, this limit should remain low (1-3 at most).
* */
* required immediately, this limit should remain low (1-3 at most). */
if (mat->default_mat != NULL && mat->default_mat != mat) {
if (mat->default_mat->pass != NULL) {
GPUShader *parent_sh = GPU_pass_shader_get(mat->default_mat->pass);
if (parent_sh) {
GPU_shader_set_parent(sh, parent_sh);
GPU_shader_warm_cache(sh, 1);
/* Skip warming if cached pass is identical to the default material. */
if (mat->default_mat->pass != mat->pass && parent_sh != sh) {
GPU_shader_set_parent(sh, parent_sh);
GPU_shader_warm_cache(sh, 1);
}
}
}
}

View File

@ -7,6 +7,7 @@
#include "BLI_utildefines.h"
#include "GPU_capabilities.h"
#include "GPU_shader.h"
/* Cache of built-in shaders (each is created on first use). */
@ -84,7 +85,8 @@ static const char *builtin_shader_create_info_name(eGPUBuiltinShader shader)
case GPU_SHADER_2D_NODELINK_INST:
return "gpu_shader_2D_nodelink_inst";
case GPU_SHADER_GPENCIL_STROKE:
return "gpu_shader_gpencil_stroke";
return GPU_geometry_shader_support() ? "gpu_shader_gpencil_stroke_geom" :
"gpu_shader_gpencil_stroke_nogeom";
default:
BLI_assert_unreachable();
return "";

View File

@ -1,12 +1,33 @@
#ifdef USE_GEOMETRY_SHADER
vec4 fragment_in_color()
{
return geometry_out.mColor;
}
vec2 fragment_in_tex_coord()
{
return geometry_out.mTexCoord;
}
#else
vec4 fragment_in_color()
{
return geometry_in.finalColor;
}
vec2 fragment_in_tex_coord()
{
return vec2(0.5);
}
#endif
void main()
{
const vec2 center = vec2(0, 0.5);
vec4 tColor = vec4(geometry_out.mColor);
vec4 tColor = fragment_in_color();
/* if alpha < 0, then encap */
if (geometry_out.mColor.a < 0) {
if (tColor.a < 0) {
tColor.a = tColor.a * -1.0;
float dist = length(geometry_out.mTexCoord - center);
float dist = length(fragment_in_tex_coord() - center);
if (dist > 0.25) {
discard;
}

View File

@ -14,13 +14,11 @@ GPU_SHADER_INTERFACE_INFO(gpencil_stroke_geom_iface, "geometry_out")
.smooth(Type::VEC4, "mColor")
.smooth(Type::VEC2, "mTexCoord");
GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke)
GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke_base)
.vertex_in(0, Type::VEC4, "color")
.vertex_in(1, Type::VEC3, "pos")
.vertex_in(2, Type::FLOAT, "thickness")
.vertex_out(gpencil_stroke_vert_iface)
.geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 13)
.geometry_out(gpencil_stroke_geom_iface)
.fragment_out(0, Type::VEC4, "fragColor")
.uniform_buf(0, "GPencilStrokeData", "gpencil_stroke_data")
@ -28,7 +26,16 @@ GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke)
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.push_constant(Type::MAT4, "ProjectionMatrix")
.vertex_source("gpu_shader_gpencil_stroke_vert.glsl")
.geometry_source("gpu_shader_gpencil_stroke_geom.glsl")
.fragment_source("gpu_shader_gpencil_stroke_frag.glsl")
.typedef_source("GPU_shader_shared.h")
.typedef_source("GPU_shader_shared.h");
GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke_geom)
.additional_info("gpu_shader_gpencil_stroke_base")
.geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 13)
.geometry_out(gpencil_stroke_geom_iface)
.geometry_source("gpu_shader_gpencil_stroke_geom.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke_nogeom)
.additional_info("gpu_shader_gpencil_stroke_base")
.do_static_compilation(true);

View File

@ -57,6 +57,7 @@ VKContext::~VKContext()
void VKContext::init_physical_device_limits()
{
BLI_assert(vk_physical_device_ != VK_NULL_HANDLE);
VkPhysicalDeviceProperties properties = {};
vkGetPhysicalDeviceProperties(vk_physical_device_, &properties);
vk_physical_device_limits_ = properties.limits;

View File

@ -48,14 +48,6 @@ void VKDescriptorSet::bind(VKStorageBuffer &buffer, const Location location)
binding.buffer_size = buffer.size_in_bytes();
}
void VKDescriptorSet::bind(VKUniformBuffer &buffer, const Location location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
binding.vk_buffer = buffer.vk_handle();
binding.buffer_size = buffer.size_in_bytes();
}
void VKDescriptorSet::bind_as_ssbo(VKVertexBuffer &buffer, const Location location)
{
Binding &binding = ensure_location(location);
@ -64,6 +56,14 @@ void VKDescriptorSet::bind_as_ssbo(VKVertexBuffer &buffer, const Location locati
binding.buffer_size = buffer.size_used_get();
}
void VKDescriptorSet::bind(VKUniformBuffer &buffer, const Location location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
binding.vk_buffer = buffer.vk_handle();
binding.buffer_size = buffer.size_in_bytes();
}
void VKDescriptorSet::bind_as_ssbo(VKIndexBuffer &buffer, const Location location)
{
Binding &binding = ensure_location(location);

View File

@ -491,6 +491,8 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
}
BKE_id_attributes_active_color_set(
&me->id, CustomData_get_layer_name(&me->ldata, CD_PROP_BYTE_COLOR, 0));
BKE_id_attributes_default_color_set(
&me->id, CustomData_get_layer_name(&me->ldata, CD_PROP_BYTE_COLOR, 0));
}
}
}

View File

@ -475,12 +475,19 @@ static USDPrimReader *get_usd_reader(CacheReader *reader,
return usd_reader;
}
USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const int read_flags)
{
USDMeshReadParams params = {};
params.motion_sample_time = motion_sample_time;
params.read_flags = read_flags;
return params;
}
struct Mesh *USD_read_mesh(struct CacheReader *reader,
struct Object *ob,
struct Mesh *existing_mesh,
const double time,
const char **err_str,
const int read_flag)
const USDMeshReadParams params,
const char **err_str)
{
USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, err_str));
@ -488,7 +495,7 @@ struct Mesh *USD_read_mesh(struct CacheReader *reader,
return nullptr;
}
return usd_reader->read_mesh(existing_mesh, time, read_flag, err_str);
return usd_reader->read_mesh(existing_mesh, params, err_str);
}
bool USD_mesh_topology_changed(CacheReader *reader,

View File

@ -162,8 +162,7 @@ void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime
}
Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh,
const double motionSampleTime,
const int /* read_flag */,
const USDMeshReadParams params,
const char ** /* err_str */)
{
if (!curve_prim_) {
@ -176,11 +175,11 @@ Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh,
pxr::VtIntArray usdCounts;
vertexAttr.Get(&usdCounts, motionSampleTime);
vertexAttr.Get(&usdCounts, params.motion_sample_time);
int num_subcurves = usdCounts.size();
pxr::VtVec3fArray usdPoints;
pointsAttr.Get(&usdPoints, motionSampleTime);
pointsAttr.Get(&usdPoints, params.motion_sample_time);
int vertex_idx = 0;
int curve_idx;
@ -204,7 +203,7 @@ Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh,
if (!same_topology) {
BKE_nurbList_free(&curve->nurb);
read_curve_sample(curve, motionSampleTime);
read_curve_sample(curve, params.motion_sample_time);
}
else {
Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);

View File

@ -36,8 +36,7 @@ class USDCurvesReader : public USDGeomReader {
void read_curve_sample(Curve *cu, double motionSampleTime);
Mesh *read_mesh(struct Mesh *existing_mesh,
double motionSampleTime,
int read_flag,
USDMeshReadParams params,
const char **err_str) override;
};

View File

@ -20,8 +20,7 @@ class USDGeomReader : public USDXformReader {
}
virtual Mesh *read_mesh(struct Mesh *existing_mesh,
double motionSampleTime,
int read_flag,
USDMeshReadParams params,
const char **err_str) = 0;
virtual bool topology_changed(const Mesh * /* existing_mesh */, double /* motionSampleTime */)

View File

@ -194,8 +194,9 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime)
Mesh *mesh = (Mesh *)object_->data;
is_initial_load_ = true;
Mesh *read_mesh = this->read_mesh(
mesh, motionSampleTime, import_params_.mesh_read_flag, nullptr);
const USDMeshReadParams params = create_mesh_read_params(motionSampleTime, import_params_.mesh_read_flag);
Mesh *read_mesh = this->read_mesh(mesh, params, nullptr);
is_initial_load_ = false;
if (read_mesh != mesh) {
@ -222,7 +223,7 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime)
}
USDXformReader::read_object_data(bmain, motionSampleTime);
}
} // namespace blender::io::usd
bool USDMeshReader::valid() const
{
@ -767,8 +768,7 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot
}
Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
const double motionSampleTime,
const int read_flag,
const USDMeshReadParams params,
const char ** /* err_str */)
{
if (!mesh_prim_) {
@ -785,7 +785,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
std::vector<pxr::TfToken> uv_tokens;
/* Currently we only handle UV primvars. */
if (read_flag & MOD_MESHSEQ_READ_UV) {
if (params.read_flags & MOD_MESHSEQ_READ_UV) {
std::vector<pxr::UsdGeomPrimvar> primvars = primvarsAPI.GetPrimvars();
@ -838,9 +838,9 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
* the topology is consistent, as in the Alembic importer. */
ImportSettings settings;
settings.read_flag |= read_flag;
settings.read_flag |= params.read_flags;
if (topology_changed(existing_mesh, motionSampleTime)) {
if (topology_changed(existing_mesh, params.motion_sample_time)) {
new_mesh = true;
active_mesh = BKE_mesh_new_nomain_from_template(
existing_mesh, positions_.size(), 0, 0, face_indices_.size(), face_counts_.size());
@ -850,7 +850,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
}
}
read_mesh_sample(&settings, active_mesh, motionSampleTime, new_mesh || is_initial_load_);
read_mesh_sample(&settings, active_mesh, params.motion_sample_time, new_mesh || is_initial_load_);
if (new_mesh) {
/* Here we assume that the number of materials doesn't change, i.e. that
@ -862,7 +862,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
bke::MutableAttributeAccessor attributes = active_mesh->attributes_for_write();
bke::SpanAttributeWriter<int> material_indices =
attributes.lookup_or_add_for_write_span<int>("material_index", ATTR_DOMAIN_FACE);
assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map);
assign_facesets_to_material_indices(params.motion_sample_time, material_indices.span, &mat_map);
material_indices.finish();
}
}

View File

@ -49,8 +49,7 @@ class USDMeshReader : public USDGeomReader {
void read_object_data(Main *bmain, double motionSampleTime) override;
struct Mesh *read_mesh(struct Mesh *existing_mesh,
double motionSampleTime,
int read_flag,
USDMeshReadParams params,
const char **err_str) override;
bool topology_changed(const Mesh *existing_mesh, double motionSampleTime) override;

View File

@ -165,8 +165,7 @@ void USDNurbsReader::read_curve_sample(Curve *cu, const double motionSampleTime)
}
Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */,
const double motionSampleTime,
const int /* read_flag */,
const USDMeshReadParams params,
const char ** /* err_str */)
{
pxr::UsdGeomCurves curve_prim_(prim_);
@ -177,11 +176,11 @@ Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */,
pxr::VtIntArray usdCounts;
vertexAttr.Get(&usdCounts, motionSampleTime);
vertexAttr.Get(&usdCounts, params.motion_sample_time);
int num_subcurves = usdCounts.size();
pxr::VtVec3fArray usdPoints;
pointsAttr.Get(&usdPoints, motionSampleTime);
pointsAttr.Get(&usdPoints, params.motion_sample_time);
int vertex_idx = 0;
int curve_idx;
@ -205,7 +204,7 @@ Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */,
if (!same_topology) {
BKE_nurbList_free(&curve->nurb);
read_curve_sample(curve, motionSampleTime);
read_curve_sample(curve, params.motion_sample_time);
}
else {
Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);

View File

@ -36,8 +36,7 @@ class USDNurbsReader : public USDGeomReader {
void read_curve_sample(Curve *cu, double motionSampleTime);
Mesh *read_mesh(struct Mesh *existing_mesh,
double motionSampleTime,
int read_flag,
USDMeshReadParams params,
const char **err_str) override;
};

View File

@ -45,9 +45,10 @@ void USDShapeReader::create_object(Main *bmain, double /*motionSampleTime*/)
void USDShapeReader::read_object_data(Main *bmain, double motionSampleTime)
{
const USDMeshReadParams params = create_mesh_read_params(motionSampleTime,
import_params_.mesh_read_flag);
Mesh *mesh = (Mesh *)object_->data;
Mesh *read_mesh = this->read_mesh(
mesh, motionSampleTime, import_params_.mesh_read_flag, nullptr);
Mesh *read_mesh = this->read_mesh(mesh, params, nullptr);
if (read_mesh != mesh) {
BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_);
@ -124,8 +125,7 @@ bool USDShapeReader::read_mesh_values(double motionSampleTime,
}
Mesh *USDShapeReader::read_mesh(struct Mesh *existing_mesh,
double motionSampleTime,
int /*read_flag*/,
const USDMeshReadParams params,
const char ** /*err_str*/)
{
pxr::VtIntArray face_indices;
@ -136,7 +136,8 @@ Mesh *USDShapeReader::read_mesh(struct Mesh *existing_mesh,
}
/* Should have a good set of data by this point-- copy over. */
Mesh *active_mesh = mesh_from_prim(existing_mesh, motionSampleTime, face_indices, face_counts);
Mesh *active_mesh = mesh_from_prim(
existing_mesh, params.motion_sample_time, face_indices, face_counts);
if (active_mesh == existing_mesh) {
return existing_mesh;
}

View File

@ -48,8 +48,7 @@ class USDShapeReader : public USDGeomReader {
void create_object(Main *bmain, double /*motionSampleTime*/) override;
void read_object_data(Main *bmain, double motionSampleTime) override;
Mesh *read_mesh(Mesh *existing_mesh,
double motionSampleTime,
int /*read_flag*/,
USDMeshReadParams params,
const char ** /*err_str*/) override;
bool is_time_varying();

View File

@ -86,6 +86,16 @@ struct USDImportParams {
bool import_all_materials;
};
/* This struct is in place to store the mesh sequence parameters needed when reading a data from a
* usd file for the mesh sequence cache.
*/
typedef struct USDMeshReadParams {
double motion_sample_time; /* USD TimeCode in frames. */
int read_flags; /* MOD_MESHSEQ_xxx value that is set from MeshSeqCacheModifierData.read_flag. */
} USDMeshReadParams;
USDMeshReadParams create_mesh_read_params(double motion_sample_time, int read_flags);
/* The USD_export takes a as_background_job parameter, and returns a boolean.
*
* When as_background_job=true, returns false immediately after scheduling
@ -121,9 +131,8 @@ void USD_get_transform(struct CacheReader *reader, float r_mat[4][4], float time
struct Mesh *USD_read_mesh(struct CacheReader *reader,
struct Object *ob,
struct Mesh *existing_mesh,
double time,
const char **err_str,
int read_flag);
USDMeshReadParams params,
const char **err_str);
bool USD_mesh_topology_changed(struct CacheReader *reader,
const struct Object *ob,

View File

@ -401,6 +401,7 @@ void MeshFromGeometry::create_colors(Mesh *mesh)
CustomDataLayer *color_layer = BKE_id_attribute_new(
&mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr);
BKE_id_attributes_active_color_set(&mesh->id, color_layer->name);
BKE_id_attributes_default_color_set(&mesh->id, color_layer->name);
float4 *colors = (float4 *)color_layer->data;
int offset = mesh_geometry_.vertex_index_min_ - block.start_vertex_index;
for (int i = 0, n = mesh_geometry_.get_vertex_count(); i != n; ++i) {

View File

@ -242,6 +242,7 @@ DEF_ENUM(rna_enum_particle_edit_hair_brush_items)
DEF_ENUM(rna_enum_particle_edit_disconnected_hair_brush_items)
DEF_ENUM(rna_enum_keyframe_paste_offset_items)
DEF_ENUM(rna_enum_keyframe_paste_offset_value)
DEF_ENUM(rna_enum_keyframe_paste_merge_items)
DEF_ENUM(rna_enum_transform_pivot_items_full)

View File

@ -2292,6 +2292,13 @@ static PointerRNA rna_Mesh_vertex_color_new(struct Mesh *me,
if (index != -1) {
ldata = rna_mesh_ldata_helper(me);
cdl = &ldata->layers[CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, index)];
if (!me->active_color_attribute) {
me->active_color_attribute = BLI_strdup(cdl->name);
}
if (!me->default_color_attribute) {
me->default_color_attribute = BLI_strdup(cdl->name);
}
}
RNA_pointer_create(&me->id, &RNA_MeshLoopColorLayer, cdl, &ptr);

View File

@ -245,12 +245,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
# endif
break;
}
case CACHEFILE_TYPE_USD:
case CACHEFILE_TYPE_USD: {
# ifdef WITH_USD
result = USD_read_mesh(
mcmd->reader, ctx->object, mesh, time * FPS, &err_str, mcmd->read_flag);
const USDMeshReadParams params = create_mesh_read_params(time * FPS, mcmd->read_flag);
result = USD_read_mesh(mcmd->reader, ctx->object, mesh, params, &err_str);
# endif
break;
}
case CACHE_FILE_TYPE_INVALID:
break;
}

View File

@ -834,15 +834,21 @@ static void initialize_group_input(NodesModifierData &nmd,
}
}
static const lf::FunctionNode &find_viewer_lf_node(const bNode &viewer_bnode)
static const lf::FunctionNode *find_viewer_lf_node(const bNode &viewer_bnode)
{
return *blender::nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree())
->mapping.viewer_node_map.lookup(&viewer_bnode);
if (const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
blender::nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree())) {
return lf_graph_info->mapping.viewer_node_map.lookup_default(&viewer_bnode, nullptr);
}
return nullptr;
}
static const lf::FunctionNode &find_group_lf_node(const bNode &group_bnode)
static const lf::FunctionNode *find_group_lf_node(const bNode &group_bnode)
{
return *blender::nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree())
->mapping.group_node_map.lookup(&group_bnode);
if (const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
blender::nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree())) {
return lf_graph_info->mapping.group_node_map.lookup_default(&group_bnode, nullptr);
}
return nullptr;
}
static void find_side_effect_nodes_for_viewer_path(
@ -888,15 +894,22 @@ static void find_side_effect_nodes_for_viewer_path(
if (found_viewer_node == nullptr) {
return;
}
const lf::FunctionNode *lf_viewer_node = find_viewer_lf_node(*found_viewer_node);
if (lf_viewer_node == nullptr) {
return;
}
/* Not only mark the viewer node as having side effects, but also all group nodes it is contained
* in. */
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
&find_viewer_lf_node(*found_viewer_node));
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), lf_viewer_node);
compute_context_builder.pop();
while (!compute_context_builder.is_empty()) {
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
&find_group_lf_node(*group_node_stack.pop()));
const lf::FunctionNode *lf_group_node = find_group_lf_node(*group_node_stack.pop());
if (lf_group_node == nullptr) {
return;
}
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), lf_group_node);
compute_context_builder.pop();
}
}

View File

@ -45,6 +45,31 @@ static Array<float> accumulated_lengths_curve_domain(const bke::CurvesGeometry &
return lengths;
}
static Array<float> calculate_curve_parameters(const bke::CurvesGeometry &curves)
{
const VArray<bool> cyclic = curves.cyclic();
Array<float> lengths = accumulated_lengths_curve_domain(curves);
const int last_index = curves.curves_num() - 1;
const float total_length = lengths.last() + curves.evaluated_length_total_for_curve(
last_index, cyclic[last_index]);
if (total_length > 0.0f) {
const float factor = 1.0f / total_length;
for (float &value : lengths) {
value *= factor;
}
}
else {
/* It is arbitrary what to do in those rare cases when all the points are
* in the same position. In this case we are just arbitrarily giving a valid
* value in the range based on the curve index. */
for (const int i : lengths.index_range()) {
lengths[i] = i / (lengths.size() - 1.0f);
}
}
return lengths;
}
/**
* Return the length of each control point along each curve, starting at zero for the first point.
* Importantly, this is different than the length at each evaluated point. The implementation is
@ -55,7 +80,9 @@ static Array<float> accumulated_lengths_curve_domain(const bke::CurvesGeometry &
* - NURBS Curves: Treat the control points as if they were a poly curve, because there
* is no obvious mapping from each control point to a specific evaluated point.
*/
static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves)
static Array<float> calculate_point_lengths(
const bke::CurvesGeometry &curves,
const FunctionRef<void(MutableSpan<float>, float)> postprocess_lengths_for_curve)
{
curves.ensure_evaluated_lengths();
const OffsetIndices points_by_curve = curves.points_by_curve();
@ -68,26 +95,31 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves)
threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) {
for (const int i_curve : range) {
const IndexRange points = points_by_curve[i_curve];
const Span<float> evaluated_lengths = curves.evaluated_lengths_for_curve(i_curve,
cyclic[i_curve]);
const bool is_cyclic = cyclic[i_curve];
const Span<float> evaluated_lengths = curves.evaluated_lengths_for_curve(i_curve, is_cyclic);
MutableSpan<float> lengths = result.as_mutable_span().slice(points);
lengths.first() = 0.0f;
float total;
switch (types[i_curve]) {
case CURVE_TYPE_CATMULL_ROM: {
const int resolution = resolutions[i_curve];
for (const int i : IndexRange(points.size()).drop_back(1)) {
lengths[i + 1] = evaluated_lengths[resolution * (i + 1) - 1];
}
total = evaluated_lengths.last();
break;
}
case CURVE_TYPE_POLY:
lengths.drop_front(1).copy_from(evaluated_lengths.take_front(lengths.size() - 1));
total = evaluated_lengths.last();
break;
case CURVE_TYPE_BEZIER: {
const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(i_curve);
for (const int i : IndexRange(points.size()).drop_back(1)) {
lengths[i + 1] = evaluated_lengths[offsets[i + 1] - 1];
}
total = evaluated_lengths.last();
break;
}
case CURVE_TYPE_NURBS: {
@ -98,115 +130,44 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves)
length += math::distance(positions[i], positions[i + 1]);
}
lengths.last() = length;
if (is_cyclic) {
length += math::distance(positions.first(), positions.last());
}
total = length;
break;
}
}
postprocess_lengths_for_curve(lengths, total);
}
});
return result;
}
static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry &curves,
const IndexMask /*mask*/,
const eAttrDomain domain)
static void convert_lengths_to_factors(MutableSpan<float> lengths, const float total_curve_length)
{
const VArray<bool> cyclic = curves.cyclic();
if (domain == ATTR_DOMAIN_POINT) {
Array<float> result = curve_length_point_domain(curves);
MutableSpan<float> lengths = result.as_mutable_span();
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) {
for (const int i_curve : range) {
MutableSpan<float> curve_lengths = lengths.slice(points_by_curve[i_curve]);
const float total_length = curve_lengths.last();
if (total_length > 0.0f) {
const float factor = 1.0f / total_length;
for (float &value : curve_lengths) {
value *= factor;
}
}
else if (curve_lengths.size() == 1) {
/* The curve is a single point. */
curve_lengths[0] = 0.0f;
}
else {
/* It is arbitrary what to do in those rare cases when all the points are
* in the same position. In this case we are just arbitrarily giving a valid
* value in the range based on the point index. */
for (const int i : curve_lengths.index_range()) {
curve_lengths[i] = i / (curve_lengths.size() - 1.0f);
}
}
}
});
return VArray<float>::ForContainer(std::move(result));
}
if (domain == ATTR_DOMAIN_CURVE) {
Array<float> lengths = accumulated_lengths_curve_domain(curves);
const int last_index = curves.curves_num() - 1;
const float total_length = lengths.last() + curves.evaluated_length_total_for_curve(
last_index, cyclic[last_index]);
if (total_length > 0.0f) {
const float factor = 1.0f / total_length;
for (float &value : lengths) {
value *= factor;
}
if (total_curve_length > 0.0f) {
const float factor = 1.0f / total_curve_length;
for (float &value : lengths.drop_front(1)) {
value *= factor;
}
else {
/* It is arbitrary what to do in those rare cases when all the points are
* in the same position. In this case we are just arbitrarily giving a valid
* value in the range based on the curve index. */
for (const int i : lengths.index_range()) {
lengths[i] = i / (lengths.size() - 1.0f);
}
}
else if (lengths.size() == 1) {
/* The curve is a single point. */
lengths[0] = 0.0f;
}
else {
/* It is arbitrary what to do in those rare cases when all the
* points are in the same position. In this case we are just
* arbitrarily giving a valid
* value in the range based on the point index. */
for (const int i : lengths.index_range()) {
lengths[i] = i / (lengths.size() - 1.0f);
}
return VArray<float>::ForContainer(std::move(lengths));
}
return {};
}
static VArray<float> construct_curve_length_parameter_varray(const bke::CurvesGeometry &curves,
const IndexMask /*mask*/,
const eAttrDomain domain)
static Array<float> calculate_point_parameters(const bke::CurvesGeometry &curves)
{
curves.ensure_evaluated_lengths();
if (domain == ATTR_DOMAIN_POINT) {
Array<float> lengths = curve_length_point_domain(curves);
return VArray<float>::ForContainer(std::move(lengths));
}
if (domain == ATTR_DOMAIN_CURVE) {
Array<float> lengths = accumulated_lengths_curve_domain(curves);
return VArray<float>::ForContainer(std::move(lengths));
}
return {};
}
static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &curves,
const IndexMask /*mask*/,
const eAttrDomain domain)
{
if (domain == ATTR_DOMAIN_POINT) {
Array<int> result(curves.points_num());
MutableSpan<int> span = result.as_mutable_span();
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) {
for (const int i_curve : range) {
MutableSpan<int> indices = span.slice(points_by_curve[i_curve]);
for (const int i : indices.index_range()) {
indices[i] = i;
}
}
});
return VArray<int>::ForContainer(std::move(result));
}
return {};
return calculate_point_lengths(curves, convert_lengths_to_factors);
}
class CurveParameterFieldInput final : public bke::CurvesFieldInput {
@ -218,14 +179,21 @@ class CurveParameterFieldInput final : public bke::CurvesFieldInput {
GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
const IndexMask /*mask*/) const final
{
return construct_curve_parameter_varray(curves, mask, domain);
switch (domain) {
case ATTR_DOMAIN_POINT:
return VArray<float>::ForContainer(calculate_point_parameters(curves));
case ATTR_DOMAIN_CURVE:
return VArray<float>::ForContainer(calculate_curve_parameters(curves));
default:
BLI_assert_unreachable();
return {};
}
}
uint64_t hash() const override
{
/* Some random constant hash. */
return 29837456298;
}
@ -245,14 +213,22 @@ class CurveLengthParameterFieldInput final : public bke::CurvesFieldInput {
GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
const IndexMask /*mask*/) const final
{
return construct_curve_length_parameter_varray(curves, mask, domain);
switch (domain) {
case ATTR_DOMAIN_POINT:
return VArray<float>::ForContainer(calculate_point_lengths(
curves, [](MutableSpan<float> /*lengths*/, const float /*total*/) {}));
case ATTR_DOMAIN_CURVE:
return VArray<float>::ForContainer(accumulated_lengths_curve_domain(curves));
default:
BLI_assert_unreachable();
return {};
}
}
uint64_t hash() const override
{
/* Some random constant hash. */
return 345634563454;
}
@ -271,14 +247,26 @@ class IndexOnSplineFieldInput final : public bke::CurvesFieldInput {
GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
const IndexMask /*mask*/) const final
{
return construct_index_on_spline_varray(curves, mask, domain);
if (domain != ATTR_DOMAIN_POINT) {
return {};
}
Array<int> result(curves.points_num());
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) {
for (const int i_curve : range) {
MutableSpan<int> indices = result.as_mutable_span().slice(points_by_curve[i_curve]);
for (const int i : indices.index_range()) {
indices[i] = i;
}
}
});
return VArray<int>::ForContainer(std::move(result));
}
uint64_t hash() const override
{
/* Some random constant hash. */
return 4536246522;
}

View File

@ -3669,22 +3669,26 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event)
}
}
static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
static eHandlerActionFlag wm_event_drag_and_drop_test(wmWindowManager *wm,
wmWindow *win,
wmEvent *event)
{
bScreen *screen = WM_window_get_active_screen(win);
if (BLI_listbase_is_empty(&wm->drags)) {
return;
return WM_HANDLER_CONTINUE;
}
if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) {
screen->do_draw_drag = true;
}
else if (event->type == EVT_ESCKEY) {
else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
wm_drags_exit(wm, win);
WM_drag_free_list(&wm->drags);
screen->do_draw_drag = true;
return WM_HANDLER_BREAK;
}
else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
event->type = EVT_DROP;
@ -3703,6 +3707,8 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
/* Restore cursor (disabled, see `wm_dragdrop.cc`) */
// WM_cursor_modal_restore(win);
}
return WM_HANDLER_CONTINUE;
}
/**
@ -4025,7 +4031,7 @@ void wm_event_do_handlers(bContext *C)
}
/* Check dragging, creates new event or frees, adds draw tag. */
wm_event_drag_and_drop_test(wm, win, event);
action |= wm_event_drag_and_drop_test(wm, win, event);
if ((action & WM_HANDLER_BREAK) == 0) {
/* NOTE: setting sub-window active should be done here,