Anim: View FCurve of Property in the Graph Editor #114407
5
AUTHORS
5
AUTHORS
|
@ -87,7 +87,7 @@ Aristotelis Dossas <teldosas>
|
|||
Arnaud Degroote <arnaud.degroote@isae-supaero.fr>
|
||||
Arno Mayrhofer <azrael3000>
|
||||
Arto Kitula <arto.kitula@gmail.com>
|
||||
Arye Ramaty <BelgaratTheGrey>
|
||||
Arye Ramaty <aryeramaty@gmail.com>
|
||||
Arystanbek Dyussenov <arystan.d@gmail.com>
|
||||
Asad-ullah Khan <kh4n>
|
||||
Asher <ThatAsherGuy>
|
||||
|
@ -189,6 +189,7 @@ Domino Marama <domino@dominodesigns.info>
|
|||
Dontsov Valentin <@blend4web.com>
|
||||
Dorian <BD3D>
|
||||
Doug Hammond <doughammond@hamsterfight.co.uk>
|
||||
Douglas Paul <douglas.w.paul@gmail.com>
|
||||
Ed Halley <ed@halley.cc>
|
||||
Edgar Roman Cervantes <redvant>
|
||||
Edmund Kapusniak <edmundmk>
|
||||
|
@ -265,6 +266,7 @@ Indy Ray <ScatteredRay>
|
|||
Inês Almeida <britalmeida@gmail.com>
|
||||
Ish Bosamiya <ish_bosamiya>
|
||||
Israel Medina <imedina>
|
||||
Ivan Kosarev <mail@ivankosarev.com>
|
||||
Ivan Perevala <ivpe>
|
||||
Iyad Ahmed <iyadahmed430@gmail.com>
|
||||
Jack Andersen <someemail@gmail.com>
|
||||
|
@ -450,6 +452,7 @@ Mike Pan <mike.c.pan@gmail.com>
|
|||
Mikhail Matrosov <ktdfly>
|
||||
Mikhail Rachinskiy <alm>
|
||||
Mikkel Gjoel <mikkelgjoel>
|
||||
Milan Davidović <milan.davidovic@protonmail.com>
|
||||
Milan Jaros <jar091>
|
||||
Mitchell Stokes <mogurijin@gmail.com>
|
||||
Monique Dewanchand <m.dewanchand@atmind.nl>
|
||||
|
|
|
@ -517,7 +517,9 @@ else()
|
|||
set(WITH_INPUT_IME OFF)
|
||||
endif()
|
||||
option(WITH_INPUT_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ON)
|
||||
if(UNIX AND NOT APPLE)
|
||||
# On Windows and for the Blender application on macOS, portable install
|
||||
# is the only supported installation type, so there is no option.
|
||||
if(UNIX AND (NOT APPLE OR WITH_PYTHON_MODULE))
|
||||
option(WITH_INSTALL_PORTABLE "\
|
||||
Install redistributable runtime, otherwise install into CMAKE_INSTALL_PREFIX"
|
||||
ON
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
"""
|
||||
File Loading & Order of Initialization
|
||||
Since drivers may be evaluated immediately after loading a blend-file it is necessary
|
||||
to ensure the driver name-space is initialized beforehand.
|
||||
|
||||
This can be done by registering text data-blocks to execute on startup,
|
||||
which executes the scripts before drivers are evaluated.
|
||||
See *Text -> Register* from Blender's text editor.
|
||||
|
||||
.. hint::
|
||||
|
||||
You may prefer to use external files instead of Blender's text-blocks.
|
||||
This can be done using a text-block which executes an external file.
|
||||
|
||||
This example runs ``driver_namespace.py`` located in the same directory as the text-blocks blend-file:
|
||||
|
||||
.. code-block::
|
||||
|
||||
import os
|
||||
import bpy
|
||||
blend_dir = os.path.normalize(os.path.join(__file__, "..", ".."))
|
||||
bpy.utils.execfile(os.path.join(blend_dir, "driver_namespace.py"))
|
||||
|
||||
Using ``__file__`` ensures the text resolves to the expected path even when library-linked from another file.
|
||||
|
||||
Other methods of populating the drivers name-space can be made to work but tend to be error prone:
|
||||
|
||||
Using The ``--python`` command line argument to populate name-space often fails to achieve the desired goal
|
||||
because the initial evaluation will lookup a function that doesn't exist yet,
|
||||
marking the driver as invalid - preventing further evaluation.
|
||||
|
||||
Populating the driver name-space before the blend-file loads also doesn't work
|
||||
since opening a file clears the name-space.
|
||||
|
||||
It is possible to run a script via the ``--python`` command line argument, before the blend file.
|
||||
This can register a load-post handler (:mod:`bpy.app.handlers.load_post`) that initialized the name-space.
|
||||
While this works for background tasks it has the downside that opening the file from the file selector
|
||||
won't setup the name-space.
|
||||
"""
|
|
@ -986,9 +986,21 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra):
|
|||
# `type_name` is only used for examples and messages:
|
||||
# `<class 'bpy.app.handlers'>` -> `bpy.app.handlers`.
|
||||
type_name = str(type(module)).strip("<>").split(" ", 1)[-1][1:-1]
|
||||
|
||||
# The type typically contains the module in the case of PyStruct's (defined by Blender).
|
||||
# Assign a temporary module name: `module_name_split`.
|
||||
if module_name == type_name:
|
||||
assert "." in module_name
|
||||
module_name_split, type_name = module_name.rpartition(".")[0::2]
|
||||
elif type_name.startswith(module_name + "."):
|
||||
type_name = type_name.removeprefix(module_name + ".")
|
||||
else:
|
||||
module_name_split = module_name
|
||||
|
||||
if type(descr) == types.GetSetDescriptorType:
|
||||
py_descr2sphinx("", fw, descr, module_name, type_name, key)
|
||||
py_descr2sphinx("", fw, descr, module_name_split, type_name, key)
|
||||
attribute_set.add(key)
|
||||
del module_name_split
|
||||
descr_sorted = []
|
||||
for key, descr in sorted(type(module).__dict__.items()):
|
||||
if key.startswith("__"):
|
||||
|
|
|
@ -238,9 +238,13 @@ static int hipewHipInit(void) {
|
|||
/* Default installation path. */
|
||||
const char *hip_paths[] = {"", NULL};
|
||||
#else
|
||||
/* ROCm 6 changes paths from /opt/rocm/hip/lib to /opt/rocm/lib, so
|
||||
* search for libraries there. It still includes .so.5. */
|
||||
const char *hip_paths[] = {"libamdhip64.so.5",
|
||||
"/opt/rocm/lib/libamdhip64.so.5",
|
||||
"/opt/rocm/hip/lib/libamdhip64.so.5",
|
||||
"libamdhip64.so",
|
||||
"/opt/rocm/lib/libamdhip64.so",
|
||||
"/opt/rocm/hip/lib/libamdhip64.so", NULL};
|
||||
#endif
|
||||
static int initialized = 0;
|
||||
|
|
|
@ -74,6 +74,9 @@ void device_metal_info(vector<DeviceInfo> &devices)
|
|||
}
|
||||
# endif
|
||||
|
||||
/* Use hardware raytracing for faster rendering on architectures that support it. */
|
||||
info.use_metalrt_by_default = (MetalInfo::get_apple_gpu_architecture(device) >= APPLE_M3);
|
||||
|
||||
devices.push_back(info);
|
||||
device_index++;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,12 @@ struct ShaderCache {
|
|||
if (MetalInfo::get_device_vendor(mtlDevice) == METAL_GPU_APPLE) {
|
||||
switch (MetalInfo::get_apple_gpu_architecture(mtlDevice)) {
|
||||
default:
|
||||
case APPLE_M3:
|
||||
/* Peak occupancy is achieved through Dynamic Caching on M3 GPUs. */
|
||||
for (size_t i = 0; i < DEVICE_KERNEL_NUM; i++) {
|
||||
occupancy_tuning[i] = {64, 64};
|
||||
}
|
||||
break;
|
||||
case APPLE_M2_BIG:
|
||||
occupancy_tuning[DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES] = {384, 128};
|
||||
occupancy_tuning[DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA] = {640, 128};
|
||||
|
|
|
@ -31,6 +31,7 @@ enum AppleGPUArchitecture {
|
|||
APPLE_M1,
|
||||
APPLE_M2,
|
||||
APPLE_M2_BIG,
|
||||
APPLE_M3,
|
||||
};
|
||||
|
||||
/* Contains static Metal helper functions. */
|
||||
|
|
|
@ -56,6 +56,9 @@ AppleGPUArchitecture MetalInfo::get_apple_gpu_architecture(id<MTLDevice> device)
|
|||
else if (strstr(device_name, "M2")) {
|
||||
return get_apple_gpu_core_count(device) <= 10 ? APPLE_M2 : APPLE_M2_BIG;
|
||||
}
|
||||
else if (strstr(device_name, "M3")) {
|
||||
return APPLE_M3;
|
||||
}
|
||||
return APPLE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
|
|
@ -504,12 +504,14 @@ if(WITH_CYCLES_CUDA_BINARIES)
|
|||
add_custom_command(
|
||||
OUTPUT ${cuda_file}
|
||||
COMMAND ${CCACHE_PROGRAM} ${cuda_nvcc_executable} ${_cuda_nvcc_args}
|
||||
DEPENDS ${kernel_sources})
|
||||
DEPENDS ${kernel_sources}
|
||||
USES_TERMINAL)
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT ${cuda_file}
|
||||
COMMAND ${cuda_nvcc_executable} ${_cuda_nvcc_args}
|
||||
DEPENDS ${kernel_sources})
|
||||
DEPENDS ${kernel_sources}
|
||||
USES_TERMINAL)
|
||||
endif()
|
||||
|
||||
unset(_cuda_nvcc_args)
|
||||
|
@ -646,7 +648,8 @@ if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP)
|
|||
add_custom_command(
|
||||
OUTPUT ${hip_file}
|
||||
COMMAND ${hip_command} ${hip_flags}
|
||||
DEPENDS ${kernel_sources})
|
||||
DEPENDS ${kernel_sources}
|
||||
USES_TERMINAL)
|
||||
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${hip_file}" ${CYCLES_INSTALL_PATH}/lib)
|
||||
list(APPEND hip_fatbins ${hip_file})
|
||||
endmacro()
|
||||
|
@ -714,7 +717,8 @@ if(WITH_CYCLES_DEVICE_HIPRT AND WITH_CYCLES_HIP_BINARIES)
|
|||
add_custom_command(
|
||||
OUTPUT ${bitcode_file}
|
||||
COMMAND ${hiprt_compile_command} ${hiprt_compile_flags}
|
||||
DEPENDS ${kernel_sources})
|
||||
DEPENDS ${kernel_sources}
|
||||
USES_TERMINAL)
|
||||
if(WIN32)
|
||||
set(hiprt_link_command ${CMAKE_COMMAND})
|
||||
set(hiprt_link_flags -E env "HIP_PATH=${HIP_ROOT_DIR}"
|
||||
|
@ -734,7 +738,8 @@ if(WITH_CYCLES_DEVICE_HIPRT AND WITH_CYCLES_HIP_BINARIES)
|
|||
add_custom_command(
|
||||
OUTPUT ${hiprt_file}
|
||||
COMMAND ${hiprt_link_command} ${hiprt_link_flags}
|
||||
DEPENDS ${bitcode_file})
|
||||
DEPENDS ${bitcode_file}
|
||||
USES_TERMINAL)
|
||||
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${hiprt_file}" ${CYCLES_INSTALL_PATH}/lib)
|
||||
add_custom_target(cycles_kernel_hiprt ALL DEPENDS ${hiprt_file})
|
||||
cycles_set_solution_folder(cycles_kernel_hiprt)
|
||||
|
@ -785,7 +790,8 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
|
|||
${cuda_flags}
|
||||
${input}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
USES_TERMINAL)
|
||||
|
||||
list(APPEND optix_ptx ${output})
|
||||
|
||||
|
@ -1069,7 +1075,8 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
|
|||
"$<$<CONFIG:Debug>:${sycl_compiler_flags_Debug}>"
|
||||
"$<$<CONFIG:MinSizeRel>:${sycl_compiler_flags_Release}>"
|
||||
COMMAND_EXPAND_LISTS
|
||||
DEPENDS ${cycles_oneapi_kernel_sources})
|
||||
DEPENDS ${cycles_oneapi_kernel_sources}
|
||||
USES_TERMINAL)
|
||||
else()
|
||||
if(NOT IGC_INSTALL_DIR)
|
||||
get_filename_component(IGC_INSTALL_DIR "${sycl_compiler_root}/../lib/igc" ABSOLUTE)
|
||||
|
@ -1094,7 +1101,8 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
|
|||
"$<$<CONFIG:Debug>:${sycl_compiler_flags_Debug_str}>"
|
||||
"$<$<CONFIG:MinSizeRel>:${sycl_compiler_flags_Release_str}>"
|
||||
COMMAND_EXPAND_LISTS
|
||||
DEPENDS ${cycles_oneapi_kernel_sources})
|
||||
DEPENDS ${cycles_oneapi_kernel_sources}
|
||||
USES_TERMINAL)
|
||||
endif()
|
||||
|
||||
if(NOT WITH_BLENDER)
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
31:Hungarian (Magyar):hu_HU
|
||||
27:Indonesian (Bahasa indonesia):id_ID
|
||||
# Skipped (see IMPORT_LANGUAGES_SKIP in settings.py). #44:Kazakh (Қазақша):kk_KZ
|
||||
50:Khmer (ខ្មែរ):km
|
||||
# Skipped (see IMPORT_LANGUAGES_SKIP in settings.py). #50:Khmer (ខ្មែរ):km
|
||||
29:Kyrgyz (Кыргыз тили):ky_KG
|
||||
25:Nepali (नेपाली):ne_NP
|
||||
3:Dutch (Nederlands):nl_NL
|
||||
|
@ -64,4 +64,4 @@
|
|||
46:Thai (ภาษาไทย):th_TH
|
||||
30:Turkish (Türkçe):tr_TR
|
||||
# Skipped (see IMPORT_LANGUAGES_SKIP in settings.py). #39:Uzbek Cyrillic (Ўзбек):uz_UZ@cyrillic
|
||||
# Skipped (see IMPORT_LANGUAGES_SKIP in settings.py). #38:Uzbek (Oʻzbek):uz_UZ@latin
|
||||
# Skipped (see IMPORT_LANGUAGES_SKIP in settings.py). #38:Uzbek (Oʻzbek):uz_UZ@latin
|
||||
|
|
|
@ -125,10 +125,10 @@ const bTheme U_theme_default = {
|
|||
.roundness = 0.2f,
|
||||
},
|
||||
.wcol_pulldown = {
|
||||
.outline = RGBA(0x3d3d3dff),
|
||||
.inner = RGBA(0x22222266),
|
||||
.inner_sel = RGBA(0x4772b3b3),
|
||||
.item = RGBA(0x727272ff),
|
||||
.outline = RGBA(0x3d3d3d00),
|
||||
.inner = RGBA(0x22222200),
|
||||
.inner_sel = RGBA(0xffffff1a),
|
||||
.item = RGBA(0xffffff8f),
|
||||
.text = RGBA(0xd9d9d9ff),
|
||||
.text_sel = RGBA(0xffffffff),
|
||||
.roundness = 0.2f,
|
||||
|
|
|
@ -104,7 +104,7 @@ IMPORT_MIN_LEVEL = 0.0
|
|||
|
||||
# Languages in the working repository that should not be imported in the Blender one currently...
|
||||
IMPORT_LANGUAGES_SKIP = {
|
||||
'am_ET', 'et_EE', 'ro_RO', 'uz_UZ@latin', 'uz_UZ@cyrillic', 'kk_KZ',
|
||||
'am_ET', 'et_EE', 'ro_RO', 'uz_UZ@latin', 'uz_UZ@cyrillic', 'kk_KZ', 'km',
|
||||
}
|
||||
|
||||
# Languages that need RTL pre-processing.
|
||||
|
|
|
@ -6078,6 +6078,8 @@ def km_edit_curves(params):
|
|||
("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'),
|
||||
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("mode", 'CURVE_SHRINKFATTEN')]}),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
|
|
@ -556,10 +556,10 @@ class GreasePencilMaterialsPanel:
|
|||
|
||||
if is_grease_pencil_version3 and ob.mode == 'EDIT':
|
||||
row = layout.row(align=True)
|
||||
row.operator("grease_pencil.stroke_change_color", text="Assign")
|
||||
row.operator("grease_pencil.stroke_material_set", text="Assign")
|
||||
elif not is_grease_pencil_version3 and ob.data.use_stroke_edit_mode:
|
||||
row = layout.row(align=True)
|
||||
row.operator("gpencil.stroke_change_color", text="Assign")
|
||||
row.operator("gpencil.stroke_material_set", text="Assign")
|
||||
row.operator("gpencil.material_select", text="Select").deselect = False
|
||||
row.operator("gpencil.material_select", text="Deselect").deselect = True
|
||||
# stroke color
|
||||
|
|
|
@ -2997,6 +2997,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
],
|
||||
'EDIT_CURVES': [
|
||||
*_tools_default,
|
||||
None,
|
||||
_defs_edit_curve.curve_radius,
|
||||
],
|
||||
'EDIT_SURFACE': [
|
||||
*_tools_default,
|
||||
|
|
|
@ -1238,7 +1238,7 @@ class VIEW3D_MT_transform(VIEW3D_MT_transform_base, Menu):
|
|||
if context.mode == 'EDIT_MESH':
|
||||
layout.operator("transform.shrink_fatten", text="Shrink/Fatten").alt_navigation = alt_navigation
|
||||
layout.operator("transform.skin_resize")
|
||||
elif context.mode in ['EDIT_CURVE', 'EDIT_GREASE_PENCIL']:
|
||||
elif context.mode in ['EDIT_CURVE', 'EDIT_GREASE_PENCIL', 'EDIT_CURVES']:
|
||||
layout.operator("transform.transform", text="Radius").mode = 'CURVE_SHRINKFATTEN'
|
||||
|
||||
if context.mode != 'EDIT_CURVES' and context.mode != 'EDIT_GREASE_PENCIL':
|
||||
|
|
|
@ -339,4 +339,4 @@ bool autokeyframe_property(bContext *C,
|
|||
return changed;
|
||||
}
|
||||
|
||||
} // namespace blender::animrig
|
||||
} // namespace blender::animrig
|
||||
|
|
|
@ -285,4 +285,4 @@ Vector<float> visualkey_get_values(PointerRNA *ptr, PropertyRNA *prop)
|
|||
/* as the function hasn't returned yet, read value from system in the default way */
|
||||
return ANIM_setting_get_rna_values(ptr, prop);
|
||||
}
|
||||
} // namespace blender::animrig
|
||||
} // namespace blender::animrig
|
||||
|
|
|
@ -470,8 +470,6 @@ void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets);
|
|||
*/
|
||||
void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh);
|
||||
|
||||
void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default);
|
||||
|
||||
/* Vertex Deformer. */
|
||||
|
||||
void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], int totvert);
|
||||
|
|
|
@ -2026,13 +2026,13 @@ static std::string unique_node_name(const GreasePencil &grease_pencil,
|
|||
static std::string unique_layer_name(const GreasePencil &grease_pencil,
|
||||
blender::StringRefNull name)
|
||||
{
|
||||
return unique_node_name(grease_pencil, DATA_("GP_Layer"), name);
|
||||
return unique_node_name(grease_pencil, DATA_("Layer"), name);
|
||||
}
|
||||
|
||||
static std::string unique_layer_group_name(const GreasePencil &grease_pencil,
|
||||
blender::StringRefNull name)
|
||||
{
|
||||
return unique_node_name(grease_pencil, DATA_("GP_Group"), name);
|
||||
return unique_node_name(grease_pencil, DATA_("Group"), name);
|
||||
}
|
||||
|
||||
blender::bke::greasepencil::Layer &GreasePencil::add_layer(const blender::StringRefNull name)
|
||||
|
|
|
@ -3619,8 +3619,11 @@ void BKE_lib_override_library_make_local(Main *bmain, ID *id)
|
|||
}
|
||||
|
||||
/* In case a liboverride hierarchy root is 'made local', i.e. is not a liboverride anymore, all
|
||||
* hierarchy roots of all liboverrides need to be validated/re-generated again. */
|
||||
BKE_lib_override_library_main_hierarchy_root_ensure(bmain);
|
||||
* hierarchy roots of all liboverrides need to be validated/re-generated again.
|
||||
* Only in case `bmain` is given, otherwise caller is responsible to do this. */
|
||||
if (bmain) {
|
||||
BKE_lib_override_library_main_hierarchy_root_ensure(bmain);
|
||||
}
|
||||
}
|
||||
|
||||
/* We only build override GHash on request. */
|
||||
|
|
|
@ -1096,7 +1096,6 @@ void bNodeTreeInterface::free_data()
|
|||
|
||||
void bNodeTreeInterface::write(BlendWriter *writer)
|
||||
{
|
||||
BLO_write_struct(writer, bNodeTreeInterface, this);
|
||||
/* Don't write the root panel struct itself, it's nested in the interface struct. */
|
||||
item_types::item_write_data(writer, this->root_panel.item);
|
||||
}
|
||||
|
|
|
@ -1756,8 +1756,6 @@ static void sculpt_update_object(
|
|||
BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets);
|
||||
BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh);
|
||||
|
||||
BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default);
|
||||
|
||||
sculpt_attribute_update_refs(ob);
|
||||
sculpt_update_persistent_base(ob);
|
||||
|
||||
|
@ -1951,28 +1949,53 @@ void BKE_sculpt_update_object_for_edit(
|
|||
|
||||
int *BKE_sculpt_face_sets_ensure(Object *ob)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
if (!attributes.contains(".sculpt_face_set")) {
|
||||
SpanAttributeWriter<int> face_sets = attributes.lookup_or_add_for_write_only_span<int>(
|
||||
".sculpt_face_set", ATTR_DOMAIN_FACE);
|
||||
face_sets.span.fill(1);
|
||||
mesh->face_sets_color_default = 1;
|
||||
face_sets.finish();
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
PBVH *pbvh = ss->pbvh;
|
||||
if (!pbvh) {
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
const StringRefNull name = ".sculpt_face_set";
|
||||
|
||||
switch (BKE_pbvh_type(pbvh)) {
|
||||
case PBVH_FACES:
|
||||
case PBVH_GRIDS: {
|
||||
MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
if (!attributes.contains(name)) {
|
||||
attributes.add<int>(name,
|
||||
ATTR_DOMAIN_FACE,
|
||||
AttributeInitVArray(VArray<int>::ForSingle(1, mesh->faces_num)));
|
||||
mesh->face_sets_color_default = 1;
|
||||
}
|
||||
|
||||
int *face_sets = static_cast<int *>(CustomData_get_layer_named_for_write(
|
||||
&mesh->face_data, CD_PROP_INT32, name.c_str(), mesh->faces_num));
|
||||
BKE_pbvh_face_sets_set(pbvh, face_sets);
|
||||
return face_sets;
|
||||
}
|
||||
case PBVH_BMESH: {
|
||||
BMesh *bm = BKE_pbvh_get_bmesh(pbvh);
|
||||
if (!CustomData_has_layer_named(&bm->pdata, CD_PROP_INT32, name.c_str())) {
|
||||
BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, name.c_str());
|
||||
const int offset = CustomData_get_offset_named(&bm->pdata, CD_PROP_INT32, name.c_str());
|
||||
if (offset == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
BMIter iter;
|
||||
BMFace *face;
|
||||
BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
|
||||
BM_ELEM_CD_SET_INT(face, offset, 1);
|
||||
}
|
||||
mesh->face_sets_color_default = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int *face_sets = static_cast<int *>(CustomData_get_layer_named_for_write(
|
||||
&mesh->face_data, CD_PROP_INT32, ".sculpt_face_set", mesh->faces_num));
|
||||
|
||||
if (ss->pbvh && ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_GRIDS)) {
|
||||
BKE_pbvh_face_sets_set(ss->pbvh, face_sets);
|
||||
}
|
||||
|
||||
return face_sets;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool *BKE_sculpt_hide_poly_ensure(Mesh *mesh)
|
||||
|
|
|
@ -665,8 +665,8 @@ static void pbvh_draw_args_init(const Mesh &mesh, PBVH *pbvh, PBVH_GPU_Args *arg
|
|||
args->node = node;
|
||||
|
||||
args->grid_hidden = pbvh->grid_hidden;
|
||||
args->face_sets_color_default = pbvh->face_sets_color_default;
|
||||
args->face_sets_color_seed = pbvh->face_sets_color_seed;
|
||||
args->face_sets_color_default = mesh.face_sets_color_default;
|
||||
args->face_sets_color_seed = mesh.face_sets_color_seed;
|
||||
args->vert_positions = pbvh->vert_positions;
|
||||
if (pbvh->mesh) {
|
||||
args->corner_verts = pbvh->corner_verts;
|
||||
|
@ -851,9 +851,6 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh)
|
|||
|
||||
pbvh->faces_num = mesh->faces_num;
|
||||
|
||||
pbvh->face_sets_color_seed = mesh->face_sets_color_seed;
|
||||
pbvh->face_sets_color_default = mesh->face_sets_color_default;
|
||||
|
||||
/* For each face, store the AABB and the AABB centroid */
|
||||
blender::Array<BBC> prim_bbc(looptri_num);
|
||||
BB cb;
|
||||
|
@ -2852,12 +2849,6 @@ void BKE_pbvh_update_normals(PBVH *pbvh, SubdivCCG *subdiv_ccg)
|
|||
}
|
||||
}
|
||||
|
||||
void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default)
|
||||
{
|
||||
pbvh->face_sets_color_seed = seed;
|
||||
pbvh->face_sets_color_default = color_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* PBVH drawing, updating draw buffers as needed and culling any nodes outside
|
||||
* the specified frustum.
|
||||
|
|
|
@ -199,7 +199,7 @@ static BMVert *bm_vert_hash_lookup_chain(GHash *deleted_verts, BMVert *v)
|
|||
|
||||
/****************************** Building ******************************/
|
||||
|
||||
/* Update node data after splitting. */
|
||||
/** Update node data after splitting. */
|
||||
static void pbvh_bmesh_node_finalize(PBVH *pbvh,
|
||||
const int node_index,
|
||||
const int cd_vert_node_offset,
|
||||
|
@ -250,7 +250,7 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh,
|
|||
n->flag |= PBVH_UpdateNormals;
|
||||
}
|
||||
|
||||
/* Recursively split the node if it exceeds the leaf_limit. */
|
||||
/** Recursively split the node if it exceeds the leaf_limit. */
|
||||
static void pbvh_bmesh_node_split(PBVH *pbvh, const Span<BBC> bbc_array, int node_index)
|
||||
{
|
||||
const int cd_vert_node_offset = pbvh->cd_vert_node_offset;
|
||||
|
@ -360,7 +360,7 @@ static void pbvh_bmesh_node_split(PBVH *pbvh, const Span<BBC> bbc_array, int nod
|
|||
n->orig_vb = n->vb;
|
||||
}
|
||||
|
||||
/* Recursively split the node if it exceeds the leaf_limit. */
|
||||
/** Recursively split the node if it exceeds the leaf_limit. */
|
||||
static bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index)
|
||||
{
|
||||
PBVHNode &node = pbvh->nodes[node_index];
|
||||
|
@ -441,7 +441,7 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh,
|
|||
|
||||
BLI_assert((pbvh->nodes.size() == 1 || node_index) && node_index <= pbvh->nodes.size());
|
||||
|
||||
/* Avoid initializing customdata because its quite involved. */
|
||||
/* Avoid initializing custom-data because its quite involved. */
|
||||
BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_NOP);
|
||||
|
||||
BM_data_interp_from_verts(pbvh->header.bm, v1, v2, v, 0.5f);
|
||||
|
@ -515,7 +515,7 @@ static int pbvh_bmesh_node_vert_use_count_at_most(PBVH *pbvh,
|
|||
return count;
|
||||
}
|
||||
|
||||
/* Return a node that uses vertex 'v' other than its current owner. */
|
||||
/** Return a node that uses vertex `v` other than its current owner. */
|
||||
static PBVHNode *pbvh_bmesh_vert_other_node_find(PBVH *pbvh, BMVert *v)
|
||||
{
|
||||
PBVHNode *current_node = pbvh_bmesh_node_from_vert(pbvh, v);
|
||||
|
@ -744,7 +744,7 @@ static bool edge_queue_tri_in_circle(const EdgeQueue *q, BMFace *f)
|
|||
return len_squared_v3v3(q->center_proj, c) <= q->radius_squared;
|
||||
}
|
||||
|
||||
/* Return true if the vertex mask is less than 1.0, false otherwise. */
|
||||
/** Return true if the vertex mask is less than 1.0, false otherwise. */
|
||||
static bool check_mask(EdgeQueueContext *eq_ctx, BMVert *v)
|
||||
{
|
||||
return BM_ELEM_CD_GET_FLOAT(v, eq_ctx->cd_vert_mask_offset) < 1.0f;
|
||||
|
@ -774,6 +774,83 @@ static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priorit
|
|||
}
|
||||
}
|
||||
|
||||
/** Return true if the edge is a boundary edge: both its vertices are on a boundary. */
|
||||
static bool is_boundary_edge(const BMEdge &edge)
|
||||
{
|
||||
if (edge.head.hflag & BM_ELEM_SEAM) {
|
||||
return true;
|
||||
}
|
||||
if ((edge.head.hflag & BM_ELEM_SMOOTH) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (!BM_edge_is_manifold(&edge)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO(@sergey): Other boundaries? For example, edges between two different face sets. */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true if the vertex is adjacent to a boundary edge. */
|
||||
static bool is_boundary_vert(const BMVert &vertex)
|
||||
{
|
||||
BMEdge *edge = vertex.e;
|
||||
BMEdge *first_edge = edge;
|
||||
do {
|
||||
if (is_boundary_edge(*edge)) {
|
||||
return true;
|
||||
}
|
||||
} while ((edge = BM_DISK_EDGE_NEXT(edge, &vertex)) != first_edge);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return true if at least one of the edge vertices is adjacent to a boundary. */
|
||||
static bool is_edge_adjacent_to_boundary(const BMEdge &edge)
|
||||
{
|
||||
return is_boundary_vert(*edge.v1) || is_boundary_vert(*edge.v2);
|
||||
}
|
||||
|
||||
/* Notes on edge priority.
|
||||
*
|
||||
* The priority is used to control the order in which edges are handled for both splitting of long
|
||||
* edges and collapsing of short edges. For long edges we start by splitting the longest edge and
|
||||
* for collapsing we start with the shortest.
|
||||
*
|
||||
* A heap-like data structure is used to accelerate such ordering. A bit confusingly, this data
|
||||
* structure gives the higher priorities to elements with lower numbers.
|
||||
*
|
||||
* When edges do not belong to and are not adjacent to boundaries, their length is used as the
|
||||
* priority directly. Prefer to handle those edges first. Modifying those edges leads to no
|
||||
* distortion to the boundary.
|
||||
*
|
||||
* Edges adjacent to a boundary with one vertex are handled next, and the vertex which is
|
||||
* on the boundary does not change position as part of the edge collapse algorithm.
|
||||
*
|
||||
* And last, the boundary edges are handled. While subdivision of boundary edges does not change
|
||||
* the shape of the boundary, collapsing boundary edges distorts the boundary. Hence they are
|
||||
* handled last. */
|
||||
|
||||
static float long_edge_queue_priority(const BMEdge &edge)
|
||||
{
|
||||
return -BM_edge_calc_length_squared(&edge);
|
||||
}
|
||||
|
||||
static float short_edge_queue_priority(const BMEdge &edge)
|
||||
{
|
||||
float priority = BM_edge_calc_length_squared(&edge);
|
||||
|
||||
if (is_boundary_edge(edge)) {
|
||||
priority *= 1.5f;
|
||||
}
|
||||
else if (is_edge_adjacent_to_boundary(edge)) {
|
||||
priority *= 1.25f;
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
static void long_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
|
||||
{
|
||||
#ifdef USE_EDGEQUEUE_TAG
|
||||
|
@ -782,7 +859,7 @@ static void long_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
|
|||
{
|
||||
const float len_sq = BM_edge_calc_length_squared(e);
|
||||
if (len_sq > eq_ctx->q->limit_len_squared) {
|
||||
edge_queue_insert(eq_ctx, e, -len_sq);
|
||||
edge_queue_insert(eq_ctx, e, long_edge_queue_priority(*e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -805,7 +882,7 @@ static void long_edge_queue_edge_add_recursive(
|
|||
if (EDGE_QUEUE_TEST(l_edge->e) == false)
|
||||
# endif
|
||||
{
|
||||
edge_queue_insert(eq_ctx, l_edge->e, -len_sq);
|
||||
edge_queue_insert(eq_ctx, l_edge->e, long_edge_queue_priority(*l_edge->e));
|
||||
}
|
||||
|
||||
/* temp support previous behavior! */
|
||||
|
@ -853,7 +930,7 @@ static void short_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
|
|||
{
|
||||
const float len_sq = BM_edge_calc_length_squared(e);
|
||||
if (len_sq < eq_ctx->q->limit_len_squared) {
|
||||
edge_queue_insert(eq_ctx, e, len_sq);
|
||||
edge_queue_insert(eq_ctx, e, short_edge_queue_priority(*e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -966,7 +1043,8 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
}
|
||||
}
|
||||
|
||||
/* Create a priority queue containing vertex pairs connected by a
|
||||
/**
|
||||
* Create a priority queue containing vertex pairs connected by a
|
||||
* short edge as defined by PBVH.bm_min_edge_len.
|
||||
*
|
||||
* Only nodes marked for topology update are checked, and in those
|
||||
|
@ -1033,6 +1111,19 @@ static void copy_edge_data(BMesh &bm, BMEdge &dst, /*const*/ BMEdge &src)
|
|||
CustomData_bmesh_copy_data(&bm.edata, &bm.edata, src.head.data, &dst.head.data);
|
||||
}
|
||||
|
||||
/* Merge edge custom data from src to dst. */
|
||||
static void merge_edge_data(BMesh &bm, BMEdge &dst, const BMEdge &src)
|
||||
{
|
||||
dst.head.hflag |= (src.head.hflag & ~(BM_ELEM_TAG | BM_ELEM_SMOOTH));
|
||||
|
||||
/* If either of the src or dst is sharp the result is sharp. */
|
||||
if ((src.head.hflag & BM_ELEM_SMOOTH) == 0) {
|
||||
dst.head.hflag &= ~BM_ELEM_SMOOTH;
|
||||
}
|
||||
|
||||
BM_data_interp_from_edges(&bm, &src, &dst, &dst, 0.5f);
|
||||
}
|
||||
|
||||
static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMEdge *e)
|
||||
{
|
||||
BMesh *bm = pbvh->header.bm;
|
||||
|
@ -1071,8 +1162,7 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMEdge *
|
|||
pbvh_bmesh_vert_ownership_transfer(pbvh, &pbvh->nodes[ni], v_new);
|
||||
}
|
||||
|
||||
/**
|
||||
* The 2 new faces created and assigned to `f_new` have their
|
||||
/* The 2 new faces created and assigned to `f_new` have their
|
||||
* verts & edges shuffled around.
|
||||
*
|
||||
* - faces wind anticlockwise in this example.
|
||||
|
@ -1188,16 +1278,279 @@ static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh
|
|||
return any_subdivided;
|
||||
}
|
||||
|
||||
/** Check whether the \a vert is adjacent to any face which are adjacent to the #edge. */
|
||||
static bool vert_in_face_adjacent_to_edge(BMVert &vert, BMEdge &edge)
|
||||
{
|
||||
BMIter bm_iter;
|
||||
BMFace *face;
|
||||
BM_ITER_ELEM (face, &bm_iter, &edge, BM_FACES_OF_EDGE) {
|
||||
if (BM_vert_in_face(&vert, face)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge attributes of a flap face into an edge which will remain after the edge collapse in
|
||||
* #pbvh_bmesh_collapse_edge.
|
||||
*
|
||||
* This function is to be called before faces adjacent to \a e are deleted.
|
||||
* This function only handles edge attributes and does not handle face deletion.
|
||||
*
|
||||
* \param del_face: Face which is adjacent to \a v_del and will form a flap when merging \a v_del
|
||||
* to \a v_conn.
|
||||
* \param flap_face: Face which is adjacent to \a v_conn and will form a flap when merging \a v_del
|
||||
* to \a v_conn.
|
||||
* \param e: An edge which is being collapsed. It connects \a v_del and \a v_conn.
|
||||
* \param v_del: A vertex which will be removed after the edge collapse.
|
||||
* \param l_del: A loop of del_face which is adjacent to v_del.
|
||||
* \param v_conn: A vertex which into which geometry is reconnected to after the edge collapse.
|
||||
*/
|
||||
static void merge_flap_edge_data(BMesh &bm,
|
||||
BMFace *del_face,
|
||||
BMFace *flap_face,
|
||||
BMEdge *e,
|
||||
BMVert *v_del,
|
||||
BMLoop *l_del,
|
||||
BMVert *v_conn)
|
||||
{
|
||||
/*
|
||||
* v_del
|
||||
* +
|
||||
* del_face . / |
|
||||
* . / |
|
||||
* . / |
|
||||
* v1 +---------------------+ v2 |
|
||||
* . \ |
|
||||
* . \ |
|
||||
* . \ |
|
||||
* flap_face +
|
||||
* v_conn
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
UNUSED_VARS_NDEBUG(del_face, flap_face);
|
||||
|
||||
/* Faces around `e` (which connects `v_del` to `v_conn`) are to the handled separately from this
|
||||
* function. Help troubleshooting cases where these faces are mistakenly considered flaps. */
|
||||
BLI_assert(!BM_edge_in_face(e, del_face));
|
||||
BLI_assert(!BM_edge_in_face(e, flap_face));
|
||||
|
||||
/* The `l_del->next->v` and `l_del->prev->v` are v1 and v2, but in an unknown order. */
|
||||
BMEdge *edge_v1_v2 = BM_edge_exists(l_del->next->v, l_del->prev->v);
|
||||
if (!edge_v1_v2) {
|
||||
CLOG_WARN(&LOG, "Unable to find edge shared between deleting and flap faces");
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(BM_edge_in_face(edge_v1_v2, del_face));
|
||||
BLI_assert(BM_edge_in_face(edge_v1_v2, flap_face));
|
||||
|
||||
/* Disambiguate v1 from v2: the v2 is adjacent to a face around #e. */
|
||||
BMVert *v2 = vert_in_face_adjacent_to_edge(*edge_v1_v2->v1, *e) ? edge_v1_v2->v1 :
|
||||
edge_v1_v2->v2;
|
||||
BMVert *v1 = BM_edge_other_vert(edge_v1_v2, v2);
|
||||
|
||||
/* Merge attributes into an edge (v1, v_conn). */
|
||||
BMEdge *dst_edge = BM_edge_exists(v1, v_conn);
|
||||
|
||||
const std::array<const BMEdge *, 4> source_edges{
|
||||
/* Edges of the `flap_face`.
|
||||
* The face will be deleted, effectively being "collapsed" into an edge. */
|
||||
edge_v1_v2,
|
||||
BM_edge_exists(v2, v_conn),
|
||||
|
||||
/* Edges of the `del_face`.
|
||||
* These edges are implicitly merged with the ones from the `flap_face` upon collapsing edge
|
||||
* `e`. */
|
||||
BM_edge_exists(v1, v_del),
|
||||
BM_edge_exists(v2, v_del),
|
||||
};
|
||||
|
||||
for (const BMEdge *src_edge : source_edges) {
|
||||
if (!src_edge) {
|
||||
CLOG_WARN(&LOG, "Unable to find source edge for flap attributes merge");
|
||||
continue;
|
||||
}
|
||||
|
||||
merge_edge_data(bm, *dst_edge, *src_edge);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find vertex which can be an outer for the flap face: the vertex will become loose when the face
|
||||
* and its edges are removed.
|
||||
* If there are multiple of such vertices, return null.
|
||||
*/
|
||||
static BMVert *find_outer_flap_vert(BMFace &face)
|
||||
{
|
||||
BMVert *flap_vert = nullptr;
|
||||
|
||||
BMIter bm_iter;
|
||||
BMVert *vert;
|
||||
BM_ITER_ELEM (vert, &bm_iter, &face, BM_VERTS_OF_FACE) {
|
||||
if (BM_vert_face_count_at_most(vert, 2) == 1) {
|
||||
if (flap_vert) {
|
||||
/* There are multiple vertices which become loose on removing the face and its edges.*/
|
||||
return nullptr;
|
||||
}
|
||||
flap_vert = vert;
|
||||
}
|
||||
}
|
||||
|
||||
return flap_vert;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the `del_face` is a flap, merge edge data from edges adjacent to "corner" vertex into the
|
||||
* other edge. The "corner" as it is an "outer", or a vertex which will become loose when the
|
||||
* `del_face` and its edges are removed.
|
||||
*
|
||||
* If the face is not a flap then this function does nothing.
|
||||
*/
|
||||
static void try_merge_flap_edge_data_before_dissolve(BMesh &bm, BMFace &face)
|
||||
{
|
||||
/*
|
||||
* v1 v2
|
||||
* ... ------ + ----------------- + ------ ...
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* + v_flap
|
||||
*/
|
||||
|
||||
BMVert *v_flap = find_outer_flap_vert(face);
|
||||
if (!v_flap) {
|
||||
return;
|
||||
}
|
||||
|
||||
BMLoop *l_flap = BM_vert_find_first_loop(v_flap);
|
||||
BLI_assert(l_flap->v == v_flap);
|
||||
|
||||
/* Edges which are adjacent ot the v_flap. */
|
||||
BMEdge *edge_1 = l_flap->prev->e;
|
||||
BMEdge *edge_2 = l_flap->e;
|
||||
|
||||
BLI_assert(BM_edge_face_count(edge_1) == 1);
|
||||
BLI_assert(BM_edge_face_count(edge_2) == 1);
|
||||
|
||||
BMEdge *edge_v1_v2 = l_flap->next->e;
|
||||
|
||||
merge_edge_data(bm, *edge_v1_v2, *edge_1);
|
||||
merge_edge_data(bm, *edge_v1_v2, *edge_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge attributes of edges from \a v_del to \a f
|
||||
*
|
||||
* This function is to be called before faces adjacent to \a e are deleted.
|
||||
* This function only handles edge attributes. and does not handle face deletion.
|
||||
*
|
||||
* \param del_face: Face which is adjacent to \a v_del and will be deleted as part of merging
|
||||
* \a v_del to \a v_conn.
|
||||
* \param new_face: A new face which is created from \a del_face by replacing \a v_del with
|
||||
* \a v_conn.
|
||||
* \param v_del: A vertex which will be removed after the edge collapse.
|
||||
* \param l_del: A loop of del_face which is adjacent to v_del.
|
||||
* \param v_conn: A vertex which into which geometry is reconnected to after the edge collapse.
|
||||
*/
|
||||
static void merge_face_edge_data(BMesh &bm,
|
||||
BMFace * /*del_face*/,
|
||||
BMFace *new_face,
|
||||
BMVert *v_del,
|
||||
BMLoop *l_del,
|
||||
BMVert *v_conn)
|
||||
{
|
||||
/* When collapsing an edge (v_conn, v_del) a face (v_conn, v2, v_del) is to be deleted and the
|
||||
* v_del reference in the face (v_del, v2, v1) is to be replaced with v_conn. Doing vertex
|
||||
* reference replacement in BMesh is not trivial. so for the simplicity the
|
||||
* #pbvh_bmesh_collapse_edge deletes both original faces and creates new one (c_conn, v2, v1).
|
||||
*
|
||||
* When doing such re-creating attributes from old edges are to be merged into the new ones:
|
||||
* - Attributes of (v_del, v1) needs to be merged into (v_conn, v1),
|
||||
* - Attributes of (v_del, v2) needs to be merged into (v_conn, v2),
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* v2
|
||||
* +
|
||||
* /|\
|
||||
* / | \
|
||||
* / | \
|
||||
* / | \
|
||||
* / | \
|
||||
* / | \
|
||||
* / | \
|
||||
* +-------+-------+
|
||||
* v_conn v_del v1
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
/* The l_del->next->v and l_del->prev->v are v1 and v2, but in an unknown order. */
|
||||
BMEdge *edge_v1_v2 = BM_edge_exists(l_del->next->v, l_del->prev->v);
|
||||
if (!edge_v1_v2) {
|
||||
CLOG_WARN(&LOG, "Unable to find edge shared between old and new faces");
|
||||
return;
|
||||
}
|
||||
|
||||
BMIter bm_iter;
|
||||
BMEdge *dst_edge;
|
||||
BM_ITER_ELEM (dst_edge, &bm_iter, new_face, BM_EDGES_OF_FACE) {
|
||||
if (dst_edge == edge_v1_v2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BLI_assert(BM_vert_in_edge(dst_edge, v_conn));
|
||||
|
||||
/* Depending on an edge v_other will be v1 or v2. */
|
||||
BMVert *v_other = BM_edge_other_vert(dst_edge, v_conn);
|
||||
|
||||
BMEdge *src_edge = BM_edge_exists(v_del, v_other);
|
||||
BLI_assert(src_edge);
|
||||
|
||||
if (src_edge) {
|
||||
merge_edge_data(bm, *dst_edge, *src_edge);
|
||||
}
|
||||
else {
|
||||
CLOG_WARN(&LOG, "Unable to find edge to merge attributes from");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pbvh_bmesh_collapse_edge(
|
||||
PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2, GHash *deleted_verts, EdgeQueueContext *eq_ctx)
|
||||
{
|
||||
BMesh &bm = *pbvh->header.bm;
|
||||
/* Prefer deleting the vertex that is less masked. */
|
||||
|
||||
const bool v1_on_boundary = is_boundary_vert(*v1);
|
||||
const bool v2_on_boundary = is_boundary_vert(*v2);
|
||||
|
||||
BMVert *v_del;
|
||||
BMVert *v_conn;
|
||||
if (BM_ELEM_CD_GET_FLOAT(v1, eq_ctx->cd_vert_mask_offset) <
|
||||
BM_ELEM_CD_GET_FLOAT(v2, eq_ctx->cd_vert_mask_offset))
|
||||
if (v1_on_boundary || v2_on_boundary) {
|
||||
/* Boundary edges can be collapsed with minimal distortion. For those it does not
|
||||
* matter too much which vertex to keep and which one to remove.
|
||||
*
|
||||
* For edges which are adjacent to boundaries, keep the vertex which is on boundary and
|
||||
* dissolve the other one. */
|
||||
if (v1_on_boundary) {
|
||||
v_del = v2;
|
||||
v_conn = v1;
|
||||
}
|
||||
else {
|
||||
v_del = v1;
|
||||
v_conn = v2;
|
||||
}
|
||||
}
|
||||
else if (BM_ELEM_CD_GET_FLOAT(v1, eq_ctx->cd_vert_mask_offset) <
|
||||
BM_ELEM_CD_GET_FLOAT(v2, eq_ctx->cd_vert_mask_offset))
|
||||
{
|
||||
/* Prefer deleting the vertex that is less masked. */
|
||||
v_del = v1;
|
||||
v_conn = v2;
|
||||
}
|
||||
|
@ -1209,6 +1562,38 @@ static void pbvh_bmesh_collapse_edge(
|
|||
/* Remove the merge vertex from the PBVH. */
|
||||
pbvh_bmesh_vert_remove(pbvh, v_del);
|
||||
|
||||
/* For all remaining faces of v_del, create a new face that is the
|
||||
* same except it uses v_conn instead of v_del */
|
||||
/* NOTE: this could be done with BM_vert_splice(), but that requires handling other issues like
|
||||
* duplicate edges, so it wouldn't really buy anything. */
|
||||
Vector<BMFace *, 16> deleted_faces;
|
||||
|
||||
BMLoop *l;
|
||||
BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
|
||||
BMFace *f_del = l->f;
|
||||
|
||||
/* Ignore faces around `e`: they will be deleted explicitly later on.
|
||||
* Without ignoring these faces the #bm_face_exists_tri_from_loop_vert() triggers an assert. */
|
||||
if (BM_edge_in_face(e, f_del)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Schedule the faces adjacent to the v_del for deletion first.
|
||||
* This way we know that it will be #existing_face which is deleted last when deleting faces
|
||||
* which forms a flap. */
|
||||
deleted_faces.append(f_del);
|
||||
|
||||
/* Check if a face using these vertices already exists. If so, skip adding this face and mark
|
||||
* the existing one for deletion as well. Prevents extraneous "flaps" from being created.
|
||||
* Check is similar to #BM_face_exists. */
|
||||
if (BMFace *existing_face = bm_face_exists_tri_from_loop_vert(l->next, v_conn)) {
|
||||
merge_flap_edge_data(bm, f_del, existing_face, e, v_del, l, v_conn);
|
||||
|
||||
deleted_faces.append(existing_face);
|
||||
}
|
||||
}
|
||||
BM_LOOPS_OF_VERT_ITER_END;
|
||||
|
||||
/* Remove all faces adjacent to the edge. */
|
||||
BMLoop *l_adj;
|
||||
while ((l_adj = e->l)) {
|
||||
|
@ -1222,23 +1607,12 @@ static void pbvh_bmesh_collapse_edge(
|
|||
BLI_assert(BM_edge_is_wire(e));
|
||||
BM_edge_kill(&bm, e);
|
||||
|
||||
/* For all remaining faces of v_del, create a new face that is the
|
||||
* same except it uses v_conn instead of v_del */
|
||||
/* NOTE: this could be done with BM_vert_splice(), but that
|
||||
* requires handling other issues like duplicate edges, so doesn't
|
||||
* really buy anything. */
|
||||
Vector<BMFace *, 16> deleted_faces;
|
||||
|
||||
BMLoop *l;
|
||||
BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
|
||||
/* Get vertices, replace use of v_del with v_conn */
|
||||
BMFace *f = l->f;
|
||||
|
||||
/* Check if a face using these vertices already exists. If so, skip adding this face and mark
|
||||
* the existing one for deletion as well. Prevents extraneous "flaps" from being created.
|
||||
* Check is similar to #BM_face_exists. */
|
||||
if (BMFace *existing_face = bm_face_exists_tri_from_loop_vert(l->next, v_conn)) {
|
||||
deleted_faces.append(existing_face);
|
||||
if (bm_face_exists_tri_from_loop_vert(l->next, v_conn)) {
|
||||
/* This case is handled above. */
|
||||
}
|
||||
else {
|
||||
const std::array<BMVert *, 3> v_tri{v_conn, l->next->v, l->prev->v};
|
||||
|
@ -1247,15 +1621,15 @@ static void pbvh_bmesh_collapse_edge(
|
|||
PBVHNode *n = pbvh_bmesh_node_from_face(pbvh, f);
|
||||
int ni = n - pbvh->nodes.data();
|
||||
const std::array<BMEdge *, 3> e_tri = bm_edges_from_tri(&bm, v_tri);
|
||||
pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f);
|
||||
BMFace *new_face = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f);
|
||||
|
||||
merge_face_edge_data(bm, f, new_face, v_del, l, v_conn);
|
||||
|
||||
/* Ensure that v_conn is in the new face's node */
|
||||
if (!n->bm_unique_verts.contains(v_conn)) {
|
||||
n->bm_other_verts.add(v_conn);
|
||||
}
|
||||
}
|
||||
|
||||
deleted_faces.append(f);
|
||||
}
|
||||
BM_LOOPS_OF_VERT_ITER_END;
|
||||
|
||||
|
@ -1267,6 +1641,11 @@ static void pbvh_bmesh_collapse_edge(
|
|||
const std::array<BMVert *, 3> v_tri{l_iter->v, l_iter->next->v, l_iter->next->next->v};
|
||||
const std::array<BMEdge *, 3> e_tri{l_iter->e, l_iter->next->e, l_iter->next->next->e};
|
||||
|
||||
/* if its sa flap face merge its "outer" edge data into "base", so that boundary is propagated
|
||||
* from edges which are about to be deleted to the base of the triangle and will stay attached
|
||||
* to the mesh. */
|
||||
try_merge_flap_edge_data_before_dissolve(bm, *f_del);
|
||||
|
||||
/* Remove the face */
|
||||
pbvh_bmesh_face_remove(pbvh, f_del);
|
||||
BM_face_kill(&bm, f_del);
|
||||
|
@ -1296,14 +1675,19 @@ static void pbvh_bmesh_collapse_edge(
|
|||
}
|
||||
}
|
||||
|
||||
/* Move v_conn to the midpoint of v_conn and v_del (if v_conn still exists, it
|
||||
* may have been deleted above). */
|
||||
if (v_conn != nullptr) {
|
||||
/* If the v_conn was not removed above move it to the midpoint of v_conn and v_del. Doing so
|
||||
* helps avoiding long stretched and degenerated triangles.
|
||||
*
|
||||
* However, if the vertex is on a boundary, do not move it to preserve the shape of the
|
||||
* boundary. */
|
||||
if (v_conn != nullptr && !is_boundary_vert(*v_conn)) {
|
||||
BM_log_vert_before_modified(pbvh->bm_log, v_conn, eq_ctx->cd_vert_mask_offset);
|
||||
mid_v3_v3v3(v_conn->co, v_conn->co, v_del->co);
|
||||
add_v3_v3(v_conn->no, v_del->no);
|
||||
normalize_v3(v_conn->no);
|
||||
}
|
||||
|
||||
if (v_conn != nullptr) {
|
||||
/* Update bounding boxes attached to the connected vertex.
|
||||
* Note that we can often get-away without this but causes #48779. */
|
||||
BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) {
|
||||
|
|
|
@ -177,8 +177,6 @@ struct PBVH {
|
|||
CustomData *loop_data;
|
||||
CustomData *face_data;
|
||||
|
||||
int face_sets_color_seed;
|
||||
int face_sets_color_default;
|
||||
int *face_sets;
|
||||
|
||||
/* Grid Data */
|
||||
|
|
|
@ -86,6 +86,8 @@ const char *dirname(char *path);
|
|||
bool BLI_windows_is_store_install(void);
|
||||
bool BLI_windows_register_blend_extension(bool all_users);
|
||||
bool BLI_windows_unregister_blend_extension(bool all_users);
|
||||
bool BLI_windows_update_pinned_launcher(const char *launcher_path);
|
||||
|
||||
/* Gets the version of the currently loaded DirectX driver for the first device that matches
|
||||
* deviceString. This is required for Qualcomm devices which use Mesa's Gallium D2D12 layer for
|
||||
* OpenGL functionality */
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief COM helper functions for windows
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
# error "This include is for Windows only!"
|
||||
#endif
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# include <windows.h>
|
||||
# undef NOMINMAX
|
||||
#else
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
class CoInitializeWrapper {
|
||||
HRESULT _hr;
|
||||
|
||||
public:
|
||||
CoInitializeWrapper(DWORD flags)
|
||||
{
|
||||
_hr = CoInitializeEx(nullptr, flags);
|
||||
}
|
||||
~CoInitializeWrapper()
|
||||
{
|
||||
if (SUCCEEDED(_hr)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
operator HRESULT()
|
||||
{
|
||||
return _hr;
|
||||
}
|
||||
};
|
||||
} // namespace blender
|
|
@ -162,7 +162,7 @@ set(SRC
|
|||
intern/voxel.c
|
||||
intern/winstuff.cc
|
||||
intern/winstuff_dir.cc
|
||||
|
||||
intern/winstuff_registration.cc
|
||||
# Private headers.
|
||||
intern/BLI_mempool_private.h
|
||||
|
||||
|
|
|
@ -194,14 +194,14 @@ ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length)
|
|||
/* Check for overlong sequences for each different length */
|
||||
switch (ab) {
|
||||
case 1:
|
||||
/* Check for: XX00 000X. */
|
||||
/* Check for: `XX00 000X`. */
|
||||
if ((c & 0x3e) == 0) {
|
||||
goto utf8_error;
|
||||
}
|
||||
continue; /* We know there aren't any more bytes to check */
|
||||
|
||||
case 2:
|
||||
/* Check for: 1110 0000, XX0X XXXX. */
|
||||
/* Check for: `1110 0000, XX0X XXXX`. */
|
||||
if (c == 0xe0 && (*p & 0x20) == 0) {
|
||||
goto utf8_error;
|
||||
}
|
||||
|
@ -243,21 +243,21 @@ ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length)
|
|||
break;
|
||||
|
||||
case 3:
|
||||
/* Check for: 1111 0000, XX00 XXXX. */
|
||||
/* Check for: `1111 0000, XX00 XXXX`. */
|
||||
if (c == 0xf0 && (*p & 0x30) == 0) {
|
||||
goto utf8_error;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
/* Check for 1111 1000, XX00 0XXX. */
|
||||
/* Check for `1111 1000, XX00 0XXX`. */
|
||||
if (c == 0xf8 && (*p & 0x38) == 0) {
|
||||
goto utf8_error;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
/* Check for: 1111 1100, XX00 00XX. */
|
||||
/* Check for: `1111 1100, XX00 00XX`. */
|
||||
if (c == 0xfc && (*p & 0x3c) == 0) {
|
||||
goto utf8_error;
|
||||
}
|
||||
|
|
|
@ -221,6 +221,11 @@ bool BLI_windows_register_blend_extension(const bool all_users)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!BLI_windows_update_pinned_launcher(blender_path)) {
|
||||
fprintf(stderr, "Update of pinned launcher failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
# ifdef WITH_BLENDER_THUMBNAILER
|
||||
{
|
||||
char reg_cmd[MAX_PATH * 2];
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef WIN32
|
||||
# include <Windows.h>
|
||||
|
||||
# include <KnownFolders.h>
|
||||
# include <filesystem>
|
||||
# include <propkey.h>
|
||||
# include <propvarutil.h>
|
||||
# include <shlobj_core.h>
|
||||
# include <wrl.h>
|
||||
|
||||
# include "BLI_path_util.h"
|
||||
# include "BLI_winstuff.h"
|
||||
# include "BLI_winstuff_com.hh"
|
||||
|
||||
# include "utf_winfunc.hh"
|
||||
# include "utfconv.hh"
|
||||
|
||||
/**
|
||||
* Pinning: Windows allows people to pin an application to their taskbar, when a user pins
|
||||
* blender, the data we set in `GHOST_WindowWin32::registerWindowAppUserModelProperties` is used
|
||||
* which includes the path to the `blender-launcher.exe`. Now once that shortcut is created on
|
||||
* the taskbar, this will never be updated, if people remove blender and install it again to a
|
||||
* different path (happens often when using nightly builds) this leads to the situation where the
|
||||
* shortcut on the taskbar points to a no longer existing blender installation. Now you may think,
|
||||
* just un-pin and re-pin that should clear that right up! It doesn't, it'll keep using the
|
||||
* outdated path till the end of time and there's no window API call we can do to update this
|
||||
* information. However this shortcut is stored in the user profile in a sub-folder we can easily
|
||||
* query, from there, we can iterate over all files, look for the one that has our APP-ID in it,
|
||||
* and when we find it, update the path to the blender launcher to the current installation, bit
|
||||
* of a hack, but Microsoft seemingly offers no other way to deal with this problem.
|
||||
*
|
||||
* this function returns true when it had no issues executing, it is NOT indicative of any changes
|
||||
* or updates being made
|
||||
*/
|
||||
bool BLI_windows_update_pinned_launcher(const char *launcher_path)
|
||||
{
|
||||
WCHAR launcher_path_w[FILE_MAX];
|
||||
|
||||
if (conv_utf_8_to_16(launcher_path, launcher_path_w, ARRAY_SIZE(launcher_path_w)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blender::CoInitializeWrapper initialize(COINIT_APARTMENTTHREADED);
|
||||
if (FAILED(initialize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LPWSTR quick_launch_folder_path;
|
||||
if (SHGetKnownFolderPath(
|
||||
FOLDERID_ImplicitAppShortcuts, KF_FLAG_DEFAULT, NULL, &quick_launch_folder_path) != S_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring search_path = quick_launch_folder_path;
|
||||
CoTaskMemFree(quick_launch_folder_path);
|
||||
|
||||
Microsoft::WRL::ComPtr<IShellLinkW> shell_link;
|
||||
if (CoCreateInstance(__uuidof(ShellLink), NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_link)) != S_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IPersistFile> persist_file;
|
||||
if (shell_link.As(&persist_file) != S_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const &dir_entry : std::filesystem::recursive_directory_iterator(search_path)) {
|
||||
if (persist_file->Load(dir_entry.path().c_str(), STGM_READWRITE) != S_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IPropertyStore> property_store;
|
||||
if (shell_link.As(&property_store) != S_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UTF16_ENCODE(BLENDER_WIN_APPID);
|
||||
PROPVARIANT app_model;
|
||||
PropVariantInit(&app_model);
|
||||
if (property_store->GetValue(PKEY_AppUserModel_ID, &app_model) == S_OK) {
|
||||
if (std::wstring(BLENDER_WIN_APPID_16) == app_model.bstrVal) {
|
||||
shell_link->SetPath(launcher_path_w);
|
||||
persist_file->Save(NULL, TRUE);
|
||||
}
|
||||
}
|
||||
PropVariantClear(&app_model);
|
||||
UTF16_UN_ENCODE(BLENDER_WIN_APPID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
|
@ -78,4 +78,4 @@ TEST(BLI_string_utils, BLI_uniquename_cb)
|
|||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
} // namespace blender
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_curve.h"
|
||||
|
@ -242,6 +244,60 @@ static void version_bonegroups_to_bonecollections(Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
static void version_principled_bsdf_update_animdata(ID *owner_id, bNodeTree *ntree)
|
||||
{
|
||||
ID *id = &ntree->id;
|
||||
AnimData *adt = BKE_animdata_from_id(id);
|
||||
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
if (node->type != SH_NODE_BSDF_PRINCIPLED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char node_name_escaped[MAX_NAME * 2];
|
||||
BLI_str_escape(node_name_escaped, node->name, sizeof(node_name_escaped));
|
||||
std::string prefix = "nodes[\"" + std::string(node_name_escaped) + "\"].inputs";
|
||||
|
||||
/* Remove animdata for inputs 18 (Transmission Roughness) and 3 (Subsurface Color). */
|
||||
BKE_animdata_fix_paths_remove(id, (prefix + "[18]").c_str());
|
||||
BKE_animdata_fix_paths_remove(id, (prefix + "[3]").c_str());
|
||||
|
||||
/* Order is important here: If we e.g. want to change A->B and B->C, but perform A->B first,
|
||||
* then later we don't know whether a B entry is an original B (and therefore should be
|
||||
* changed to C) or used to be A and was already handled.
|
||||
* In practice, going reverse mostly works, the two notable dependency chains are:
|
||||
* - 8->13, then 2->8, then 9->2 (13 was changed before)
|
||||
* - 1->9, then 6->1 (9 was changed before)
|
||||
* - 4->10, then 21->4 (10 was changed before)
|
||||
*
|
||||
* 0 (Base Color) and 17 (Transmission) are fine as-is. */
|
||||
std::pair<int, int> remap_table[] = {
|
||||
{20, 27}, /* Emission Strength */
|
||||
{19, 26}, /* Emission */
|
||||
{16, 3}, /* IOR */
|
||||
{15, 19}, /* Clearcoat Roughness */
|
||||
{14, 18}, /* Clearcoat */
|
||||
{13, 25}, /* Sheen Tint */
|
||||
{12, 23}, /* Sheen */
|
||||
{11, 15}, /* Anisotropic Rotation */
|
||||
{10, 14}, /* Anisotropic */
|
||||
{8, 13}, /* Specular Tint */
|
||||
{2, 8}, /* Subsurface Radius */
|
||||
{9, 2}, /* Roughness */
|
||||
{7, 12}, /* Specular */
|
||||
{1, 9}, /* Subsurface Scale */
|
||||
{6, 1}, /* Metallic */
|
||||
{5, 11}, /* Subsurface Anisotropy */
|
||||
{4, 10}, /* Subsurface IOR */
|
||||
{21, 4} /* Alpha */
|
||||
};
|
||||
for (const auto &entry : remap_table) {
|
||||
BKE_animdata_fix_paths_rename(
|
||||
id, adt, owner_id, prefix.c_str(), nullptr, nullptr, entry.first, entry.second, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_versions_after_linking_400(FileData *fd, Main *bmain)
|
||||
{
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 9)) {
|
||||
|
@ -312,6 +368,16 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 24)) {
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
if (ntree->type == NTREE_SHADER) {
|
||||
/* Convert animdata on the Principled BSDF sockets. */
|
||||
version_principled_bsdf_update_animdata(id, ntree);
|
||||
}
|
||||
}
|
||||
FOREACH_NODETREE_END;
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 34)) {
|
||||
BKE_mesh_legacy_face_map_to_generic(bmain);
|
||||
}
|
||||
|
@ -1744,5 +1810,21 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
MA_SURFACE_METHOD_DEFERRED;
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
|
||||
const ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
|
||||
&sl->regionbase;
|
||||
LISTBASE_FOREACH (ARegion *, region, regionbase) {
|
||||
if (region->regiontype != RGN_TYPE_ASSET_SHELF_HEADER) {
|
||||
continue;
|
||||
}
|
||||
region->alignment &= ~RGN_SPLIT_PREV;
|
||||
region->alignment |= RGN_ALIGN_HIDE_WITH_PREV;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,10 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
|
|||
FROM_DEFAULT_V4_UCHAR(space_sequencer.transition);
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(400, 35)) {
|
||||
memcpy(btheme, &U_theme_default, sizeof(*btheme));
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
|
|
@ -208,7 +208,6 @@ class ZstdWriteWrap : public WriteWrap {
|
|||
int next_frame = 0;
|
||||
int num_frames = 0;
|
||||
|
||||
int level = 0;
|
||||
ListBase frames = {};
|
||||
|
||||
bool write_error = false;
|
||||
|
|
|
@ -345,6 +345,8 @@ if(WITH_COMPOSITOR_CPU)
|
|||
operations/COM_GaussianXBlurOperation.h
|
||||
operations/COM_GaussianYBlurOperation.cc
|
||||
operations/COM_GaussianYBlurOperation.h
|
||||
operations/COM_SummedAreaTableOperation.h
|
||||
operations/COM_SummedAreaTableOperation.cc
|
||||
operations/COM_KuwaharaAnisotropicOperation.cc
|
||||
operations/COM_KuwaharaAnisotropicOperation.h
|
||||
operations/COM_KuwaharaAnisotropicStructureTensorOperation.cc
|
||||
|
@ -665,6 +667,7 @@ if(WITH_COMPOSITOR_CPU)
|
|||
tests/COM_BufferRange_test.cc
|
||||
tests/COM_BuffersIterator_test.cc
|
||||
tests/COM_NodeOperation_test.cc
|
||||
tests/COM_ComputeSummedAreaTableOperation_test.cc
|
||||
)
|
||||
set(TEST_INC
|
||||
)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "COM_KuwaharaAnisotropicOperation.h"
|
||||
#include "COM_KuwaharaAnisotropicStructureTensorOperation.h"
|
||||
#include "COM_KuwaharaClassicOperation.h"
|
||||
#include "COM_SummedAreaTableOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
|
@ -23,12 +24,24 @@ void KuwaharaNode::convert_to_operations(NodeConverter &converter,
|
|||
|
||||
switch (data->variation) {
|
||||
case CMP_NODE_KUWAHARA_CLASSIC: {
|
||||
KuwaharaClassicOperation *operation = new KuwaharaClassicOperation();
|
||||
KuwaharaClassicOperation *kuwahara_classic = new KuwaharaClassicOperation();
|
||||
converter.add_operation(kuwahara_classic);
|
||||
converter.map_input_socket(get_input_socket(0), kuwahara_classic->get_input_socket(0));
|
||||
converter.map_input_socket(get_input_socket(1), kuwahara_classic->get_input_socket(1));
|
||||
|
||||
converter.add_operation(operation);
|
||||
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
|
||||
converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1));
|
||||
converter.map_output_socket(get_output_socket(0), operation->get_output_socket());
|
||||
SummedAreaTableOperation *sat = new SummedAreaTableOperation();
|
||||
sat->set_mode(SummedAreaTableOperation::eMode::Identity);
|
||||
converter.add_operation(sat);
|
||||
converter.map_input_socket(get_input_socket(0), sat->get_input_socket(0));
|
||||
converter.add_link(sat->get_output_socket(0), kuwahara_classic->get_input_socket(2));
|
||||
|
||||
SummedAreaTableOperation *sat_squared = new SummedAreaTableOperation();
|
||||
sat_squared->set_mode(SummedAreaTableOperation::eMode::Squared);
|
||||
converter.add_operation(sat_squared);
|
||||
converter.map_input_socket(get_input_socket(0), sat_squared->get_input_socket(0));
|
||||
converter.add_link(sat_squared->get_output_socket(0), kuwahara_classic->get_input_socket(3));
|
||||
|
||||
converter.map_output_socket(get_output_socket(0), kuwahara_classic->get_output_socket(0));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ KuwaharaClassicOperation::KuwaharaClassicOperation()
|
|||
{
|
||||
this->add_input_socket(DataType::Color);
|
||||
this->add_input_socket(DataType::Value);
|
||||
this->add_input_socket(DataType::Color);
|
||||
this->add_input_socket(DataType::Color);
|
||||
this->add_output_socket(DataType::Color);
|
||||
|
||||
this->flags_.is_fullframe_operation = true;
|
||||
|
@ -25,12 +27,16 @@ void KuwaharaClassicOperation::init_execution()
|
|||
{
|
||||
image_reader_ = this->get_input_socket_reader(0);
|
||||
size_reader_ = this->get_input_socket_reader(1);
|
||||
sat_reader_ = this->get_input_socket_reader(2);
|
||||
sat_squared_reader_ = this->get_input_socket_reader(3);
|
||||
}
|
||||
|
||||
void KuwaharaClassicOperation::deinit_execution()
|
||||
{
|
||||
image_reader_ = nullptr;
|
||||
size_reader_ = nullptr;
|
||||
sat_reader_ = nullptr;
|
||||
sat_squared_reader_ = nullptr;
|
||||
}
|
||||
|
||||
void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
|
||||
|
@ -46,13 +52,44 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
|
|||
size_reader_->read_sampled(size, x, y, sampler);
|
||||
const int kernel_size = int(math::max(0.0f, size[0]));
|
||||
|
||||
/* Split surroundings of pixel into 4 overlapping regions. */
|
||||
for (int dy = -kernel_size; dy <= kernel_size; dy++) {
|
||||
for (int dx = -kernel_size; dx <= kernel_size; dx++) {
|
||||
/* Naive implementation is more accurate for small kernel sizes. */
|
||||
if (kernel_size >= 4) {
|
||||
for (int q = 0; q < 4; q++) {
|
||||
/* A fancy expression to compute the sign of the quadrant q. */
|
||||
int2 sign = int2((q % 2) * 2 - 1, ((q / 2) * 2 - 1));
|
||||
|
||||
int xx = x + dx;
|
||||
int yy = y + dy;
|
||||
if (xx >= 0 && yy >= 0 && xx < this->get_width() && yy < this->get_height()) {
|
||||
int2 lower_bound = int2(x, y) -
|
||||
int2(sign.x > 0 ? 0 : kernel_size, sign.y > 0 ? 0 : kernel_size);
|
||||
int2 upper_bound = int2(x, y) +
|
||||
int2(sign.x < 0 ? 0 : kernel_size, sign.y < 0 ? 0 : kernel_size);
|
||||
|
||||
/* Limit the quadrants to the image bounds. */
|
||||
int2 image_bound = int2(this->get_width(), this->get_height()) - int2(1);
|
||||
int2 corrected_lower_bound = math::min(image_bound, math::max(int2(0, 0), lower_bound));
|
||||
int2 corrected_upper_bound = math::min(image_bound, math::max(int2(0, 0), upper_bound));
|
||||
int2 region_size = corrected_upper_bound - corrected_lower_bound + int2(1, 1);
|
||||
quadrant_pixel_count[q] = region_size.x * region_size.y;
|
||||
|
||||
rcti kernel_area;
|
||||
kernel_area.xmin = corrected_lower_bound[0];
|
||||
kernel_area.ymin = corrected_lower_bound[1];
|
||||
kernel_area.xmax = corrected_upper_bound[0];
|
||||
kernel_area.ymax = corrected_upper_bound[1];
|
||||
|
||||
mean_of_color[q] = summed_area_table_sum_tiled(sat_reader_, kernel_area);
|
||||
mean_of_squared_color[q] = summed_area_table_sum_tiled(sat_squared_reader_, kernel_area);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Split surroundings of pixel into 4 overlapping regions. */
|
||||
for (int dy = -kernel_size; dy <= kernel_size; dy++) {
|
||||
for (int dx = -kernel_size; dx <= kernel_size; dx++) {
|
||||
|
||||
int xx = x + dx;
|
||||
int yy = y + dy;
|
||||
if (xx < 0 || yy < 0 || xx >= this->get_width() || yy >= this->get_height()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float4 color;
|
||||
image_reader_->read_sampled(color, xx, yy, sampler);
|
||||
|
@ -115,24 +152,60 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
|
|||
{
|
||||
MemoryBuffer *image = inputs[0];
|
||||
MemoryBuffer *size_image = inputs[1];
|
||||
MemoryBuffer *sat = inputs[2];
|
||||
MemoryBuffer *sat_squared = inputs[3];
|
||||
|
||||
int width = image->get_width();
|
||||
int height = image->get_height();
|
||||
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const int x = it.x;
|
||||
const int y = it.y;
|
||||
|
||||
float4 mean_of_color[] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
|
||||
float4 mean_of_squared_color[] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
|
||||
int quadrant_pixel_count[] = {0, 0, 0, 0};
|
||||
float4 mean_of_color[4] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
|
||||
float4 mean_of_squared_color[4] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
|
||||
int quadrant_pixel_count[4] = {0, 0, 0, 0};
|
||||
|
||||
const int kernel_size = int(math::max(0.0f, *size_image->get_elem(x, y)));
|
||||
|
||||
/* Split surroundings of pixel into 4 overlapping regions. */
|
||||
for (int dy = -kernel_size; dy <= kernel_size; dy++) {
|
||||
for (int dx = -kernel_size; dx <= kernel_size; dx++) {
|
||||
/* Naive implementation is more accurate for small kernel sizes. */
|
||||
if (kernel_size >= 4) {
|
||||
for (int q = 0; q < 4; q++) {
|
||||
/* A fancy expression to compute the sign of the quadrant q. */
|
||||
int2 sign = int2((q % 2) * 2 - 1, ((q / 2) * 2 - 1));
|
||||
|
||||
int xx = x + dx;
|
||||
int yy = y + dy;
|
||||
if (xx >= 0 && yy >= 0 && xx < image->get_width() && yy < image->get_height()) {
|
||||
int2 lower_bound = int2(x, y) -
|
||||
int2(sign.x > 0 ? 0 : kernel_size, sign.y > 0 ? 0 : kernel_size);
|
||||
int2 upper_bound = int2(x, y) +
|
||||
int2(sign.x < 0 ? 0 : kernel_size, sign.y < 0 ? 0 : kernel_size);
|
||||
|
||||
/* Limit the quadrants to the image bounds. */
|
||||
int2 image_bound = int2(width, height) - int2(1);
|
||||
int2 corrected_lower_bound = math::min(image_bound, math::max(int2(0, 0), lower_bound));
|
||||
int2 corrected_upper_bound = math::min(image_bound, math::max(int2(0, 0), upper_bound));
|
||||
int2 region_size = corrected_upper_bound - corrected_lower_bound + int2(1, 1);
|
||||
quadrant_pixel_count[q] = region_size.x * region_size.y;
|
||||
|
||||
rcti kernel_area;
|
||||
kernel_area.xmin = corrected_lower_bound[0];
|
||||
kernel_area.ymin = corrected_lower_bound[1];
|
||||
kernel_area.xmax = corrected_upper_bound[0];
|
||||
kernel_area.ymax = corrected_upper_bound[1];
|
||||
|
||||
mean_of_color[q] = summed_area_table_sum(sat, kernel_area);
|
||||
mean_of_squared_color[q] = summed_area_table_sum(sat_squared, kernel_area);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Split surroundings of pixel into 4 overlapping regions. */
|
||||
for (int dy = -kernel_size; dy <= kernel_size; dy++) {
|
||||
for (int dx = -kernel_size; dx <= kernel_size; dx++) {
|
||||
|
||||
int xx = x + dx;
|
||||
int yy = y + dy;
|
||||
if (xx < 0 || yy < 0 || xx >= image->get_width() || yy >= image->get_height()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float4 color;
|
||||
image->read_elem(xx, yy, &color.x);
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace blender::compositor {
|
|||
class KuwaharaClassicOperation : public MultiThreadedOperation {
|
||||
SocketReader *image_reader_;
|
||||
SocketReader *size_reader_;
|
||||
SocketReader *sat_reader_;
|
||||
SocketReader *sat_squared_reader_;
|
||||
|
||||
public:
|
||||
KuwaharaClassicOperation();
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "COM_SummedAreaTableOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
SummedAreaTableOperation::SummedAreaTableOperation()
|
||||
{
|
||||
this->add_input_socket(DataType::Color);
|
||||
this->add_output_socket(DataType::Color);
|
||||
|
||||
mode_ = eMode::Identity;
|
||||
|
||||
this->flags_.is_fullframe_operation = true;
|
||||
}
|
||||
|
||||
void SummedAreaTableOperation::init_execution()
|
||||
{
|
||||
SingleThreadedOperation::init_execution();
|
||||
image_reader_ = this->get_input_socket_reader(0);
|
||||
}
|
||||
|
||||
void SummedAreaTableOperation::deinit_execution()
|
||||
{
|
||||
image_reader_ = nullptr;
|
||||
SingleThreadedOperation::deinit_execution();
|
||||
}
|
||||
|
||||
bool SummedAreaTableOperation::determine_depending_area_of_interest(
|
||||
rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output)
|
||||
{
|
||||
rcti image_input;
|
||||
|
||||
NodeOperation *operation = get_input_operation(0);
|
||||
image_input.xmax = operation->get_width();
|
||||
image_input.xmin = 0;
|
||||
image_input.ymax = operation->get_height();
|
||||
image_input.ymin = 0;
|
||||
if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SummedAreaTableOperation::get_area_of_interest(int input_idx,
|
||||
const rcti & /*output_area*/,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
r_input_area = get_input_operation(input_idx)->get_canvas();
|
||||
}
|
||||
|
||||
void SummedAreaTableOperation::update_memory_buffer(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
/* Note: although this is a single threaded call, multithreading is used. */
|
||||
MemoryBuffer *image = inputs[0];
|
||||
|
||||
/* First pass: copy input to output and sum horizontally. */
|
||||
threading::parallel_for(IndexRange(area.ymin, area.ymax), 1, [&](const IndexRange range_y) {
|
||||
for (const int y : range_y) {
|
||||
float4 accumulated_color = float4(0.0f);
|
||||
for (const int x : IndexRange(area.xmin, area.xmax)) {
|
||||
const float4 color = float4(image->get_elem(x, y));
|
||||
accumulated_color += mode_ == eMode::Squared ? color * color : color;
|
||||
copy_v4_v4(output->get_elem(x, y), accumulated_color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Second pass: vertical sum. */
|
||||
threading::parallel_for(IndexRange(area.xmin, area.xmax), 1, [&](const IndexRange range_x) {
|
||||
for (const int x : range_x) {
|
||||
float4 accumulated_color = float4(0.0f);
|
||||
for (const int y : IndexRange(area.ymin, area.ymax)) {
|
||||
const float4 color = float4(output->get_elem(x, y));
|
||||
accumulated_color += color;
|
||||
copy_v4_v4(output->get_elem(x, y), accumulated_color);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MemoryBuffer *SummedAreaTableOperation::create_memory_buffer(rcti *area)
|
||||
{
|
||||
/* Note: although this is a single threaded call, multithreading is used. */
|
||||
MemoryBuffer *output = new MemoryBuffer(DataType::Color, *area);
|
||||
|
||||
/* First pass: copy input to output and sum horizontally. */
|
||||
threading::parallel_for(IndexRange(area->ymin, area->ymax), 1, [&](const IndexRange range_y) {
|
||||
for (const int y : range_y) {
|
||||
float4 accumulated_color = float4(0.0f);
|
||||
for (const int x : IndexRange(area->xmin, area->xmax)) {
|
||||
float4 color;
|
||||
image_reader_->read(&color.x, x, y, nullptr);
|
||||
accumulated_color += mode_ == eMode::Squared ? color * color : color;
|
||||
copy_v4_v4(output->get_elem(x, y), accumulated_color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Second pass: vertical sum. */
|
||||
threading::parallel_for(IndexRange(area->xmin, area->xmax), 1, [&](const IndexRange range_x) {
|
||||
for (const int x : range_x) {
|
||||
float4 accumulated_color = float4(0.0f);
|
||||
for (const int y : IndexRange(area->ymin, area->ymax)) {
|
||||
|
||||
accumulated_color += float4(output->get_elem(x, y));
|
||||
copy_v4_v4(output->get_elem(x, y), accumulated_color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void SummedAreaTableOperation::set_mode(eMode mode)
|
||||
{
|
||||
mode_ = mode;
|
||||
}
|
||||
|
||||
SummedAreaTableOperation::eMode SummedAreaTableOperation::get_mode()
|
||||
{
|
||||
return mode_;
|
||||
}
|
||||
|
||||
float4 summed_area_table_sum_tiled(SocketReader *buffer, const rcti &area)
|
||||
{
|
||||
/*
|
||||
* a, b, c and d are the bounding box of the given area. They are defined as follows:
|
||||
*
|
||||
* y
|
||||
* ▲
|
||||
* │
|
||||
* ├──────x───────x
|
||||
* │ │c d│
|
||||
* ├──────x───────x
|
||||
* │ │a b│
|
||||
* └──────┴───────┴──────► x
|
||||
*
|
||||
* Note: this is the same definition as in https://en.wikipedia.org/wiki/Summed-area_table
|
||||
* but using the blender convention with the origin being at the lower left.
|
||||
*/
|
||||
|
||||
BLI_assert(area.xmin <= area.xmax && area.ymin <= area.ymax);
|
||||
|
||||
int2 lower_bound(area.xmin, area.ymin);
|
||||
int2 upper_bound(area.xmax, area.ymax);
|
||||
|
||||
int2 corrected_lower_bound = lower_bound - int2(1, 1);
|
||||
int2 corrected_upper_bound;
|
||||
corrected_upper_bound[0] = math::min((int)buffer->get_width() - 1, upper_bound[0]);
|
||||
corrected_upper_bound[1] = math::min((int)buffer->get_height() - 1, upper_bound[1]);
|
||||
|
||||
float4 a, b, c, d, addend, substrahend;
|
||||
buffer->read_sampled(
|
||||
&a.x, corrected_upper_bound[0], corrected_upper_bound[1], PixelSampler::Nearest);
|
||||
buffer->read_sampled(
|
||||
&d.x, corrected_lower_bound[0], corrected_lower_bound[1], PixelSampler::Nearest);
|
||||
addend = a + d;
|
||||
|
||||
buffer->read_sampled(
|
||||
&b.x, corrected_lower_bound[0], corrected_upper_bound[1], PixelSampler::Nearest);
|
||||
buffer->read_sampled(
|
||||
&c.x, corrected_upper_bound[0], corrected_lower_bound[1], PixelSampler::Nearest);
|
||||
substrahend = b + c;
|
||||
|
||||
float4 sum = addend - substrahend;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
float4 summed_area_table_sum(MemoryBuffer *buffer, const rcti &area)
|
||||
{
|
||||
/*
|
||||
* a, b, c and d are the bounding box of the given area. They are defined as follows:
|
||||
*
|
||||
* y
|
||||
* ▲
|
||||
* │
|
||||
* ├──────x───────x
|
||||
* │ │c d│
|
||||
* ├──────x───────x
|
||||
* │ │a b│
|
||||
* └──────┴───────┴──────► x
|
||||
*
|
||||
* Note: this is the same definition as in https://en.wikipedia.org/wiki/Summed-area_table
|
||||
* but using the blender convention with the origin being at the lower left.
|
||||
*/
|
||||
|
||||
BLI_assert(area.xmin <= area.xmax && area.ymin <= area.ymax);
|
||||
|
||||
int2 lower_bound(area.xmin, area.ymin);
|
||||
int2 upper_bound(area.xmax, area.ymax);
|
||||
|
||||
int2 corrected_lower_bound = lower_bound - int2(1, 1);
|
||||
int2 corrected_upper_bound;
|
||||
corrected_upper_bound[0] = math::min(buffer->get_width() - 1, upper_bound[0]);
|
||||
corrected_upper_bound[1] = math::min(buffer->get_height() - 1, upper_bound[1]);
|
||||
|
||||
float4 a, b, c, d, addend, substrahend;
|
||||
buffer->read_elem_checked(corrected_upper_bound[0], corrected_upper_bound[1], a);
|
||||
buffer->read_elem_checked(corrected_lower_bound[0], corrected_lower_bound[1], d);
|
||||
addend = a + d;
|
||||
|
||||
buffer->read_elem_checked(corrected_lower_bound[0], corrected_upper_bound[1], b);
|
||||
buffer->read_elem_checked(corrected_upper_bound[0], corrected_lower_bound[1], c);
|
||||
substrahend = b + c;
|
||||
|
||||
float4 sum = addend - substrahend;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_SingleThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
/**
|
||||
* \brief SummedAreaTableOperation class computes the summed area table.
|
||||
*/
|
||||
class SummedAreaTableOperation : public SingleThreadedOperation {
|
||||
|
||||
public:
|
||||
SummedAreaTableOperation();
|
||||
|
||||
enum eMode { Identity = 1, Squared };
|
||||
|
||||
void set_mode(const eMode mode);
|
||||
eMode get_mode();
|
||||
|
||||
/**
|
||||
* Initialize the execution
|
||||
*/
|
||||
void init_execution() override;
|
||||
|
||||
/**
|
||||
* Deinitialize the execution
|
||||
*/
|
||||
void deinit_execution() override;
|
||||
|
||||
bool determine_depending_area_of_interest(rcti *input,
|
||||
ReadBufferOperation *read_operation,
|
||||
rcti *output) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
|
||||
MemoryBuffer *create_memory_buffer(rcti *rect) override;
|
||||
|
||||
void update_memory_buffer(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
|
||||
private:
|
||||
SocketReader *image_reader_;
|
||||
eMode mode_;
|
||||
};
|
||||
|
||||
/* Computes the sum of the rectangular region defined by the given area from the
|
||||
* given summed area table. All coordinates within the area are included. */
|
||||
float4 summed_area_table_sum(MemoryBuffer *buffer, const rcti &area);
|
||||
float4 summed_area_table_sum_tiled(SocketReader *buffer, const rcti &area);
|
||||
|
||||
} // namespace blender::compositor
|
|
@ -0,0 +1,191 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "COM_SummedAreaTableOperation.h"
|
||||
|
||||
namespace blender::compositor::tests {
|
||||
|
||||
struct SatParams {
|
||||
/* Input parameters. */
|
||||
SummedAreaTableOperation::eMode mode;
|
||||
eExecutionModel execution_model;
|
||||
rcti area;
|
||||
float4 fill_value;
|
||||
|
||||
/* Expected output values. */
|
||||
std::vector<std::vector<float>> values;
|
||||
};
|
||||
|
||||
class SummedAreaTableTestP : public testing::TestWithParam<SatParams> {
|
||||
};
|
||||
|
||||
TEST_P(SummedAreaTableTestP, Values)
|
||||
{
|
||||
SatParams params = GetParam();
|
||||
|
||||
SummedAreaTableOperation sat = SummedAreaTableOperation();
|
||||
|
||||
sat.set_execution_model(params.execution_model);
|
||||
sat.set_mode(params.mode);
|
||||
const rcti area = params.area;
|
||||
MemoryBuffer output(DataType::Color, area);
|
||||
|
||||
std::shared_ptr<MemoryBuffer> input = std::make_shared<MemoryBuffer>(DataType::Color, area);
|
||||
input->fill(area, ¶ms.fill_value.x);
|
||||
|
||||
sat.update_memory_buffer(&output, area, Span<MemoryBuffer *>{input.get()});
|
||||
|
||||
/* First row. */
|
||||
EXPECT_FLOAT_EQ(output.get_elem(0, 0)[0], params.values[0][0]);
|
||||
EXPECT_FLOAT_EQ(output.get_elem(1, 0)[1], params.values[0][1]);
|
||||
EXPECT_FLOAT_EQ(output.get_elem(2, 0)[2], params.values[0][2]);
|
||||
|
||||
/* Second row. */
|
||||
EXPECT_FLOAT_EQ(output.get_elem(0, 1)[3], params.values[1][0]);
|
||||
EXPECT_FLOAT_EQ(output.get_elem(1, 1)[0], params.values[1][1]);
|
||||
EXPECT_FLOAT_EQ(output.get_elem(2, 1)[1], params.values[1][2]);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(FullFrame5x2_IdentityOnes,
|
||||
SummedAreaTableTestP,
|
||||
testing::Values(SatParams{
|
||||
SummedAreaTableOperation::eMode::Identity,
|
||||
eExecutionModel::FullFrame,
|
||||
rcti{0, 5, 0, 2}, /* Area. */
|
||||
{1.0f, 1.0f, 1.0f, 1.0f}, /* Fill value. */
|
||||
|
||||
/* Expected output. */
|
||||
{{1.0f, 2.0f, 3.0f, 4.0f, 5.0f}, {2.0f, 4.0f, 6.0f, 8.0f, 10.0f}}
|
||||
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
FullFrame5x2_SquaredOnes,
|
||||
SummedAreaTableTestP,
|
||||
testing::Values(SatParams{
|
||||
SummedAreaTableOperation::eMode::Squared,
|
||||
eExecutionModel::FullFrame,
|
||||
rcti{0, 5, 0, 2}, /* Area. */
|
||||
{1.0f, 1.0f, 1.0f, 1.0f}, /* Fill value. */
|
||||
|
||||
/* Expect identical to when using Identity SAT, since all inputs are 1. */
|
||||
{{1.0f, 2.0f, 3.0f, 4.0f, 5.0f}, {2.0f, 4.0f, 6.0f, 8.0f, 10.0f}}
|
||||
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(FullFrame3x2_Squared,
|
||||
SummedAreaTableTestP,
|
||||
testing::Values(SatParams{SummedAreaTableOperation::eMode::Squared,
|
||||
eExecutionModel::FullFrame,
|
||||
rcti{0, 3, 0, 2}, /* Area. */
|
||||
{2.0f, 2.0f, 1.5f, .1f}, /* Fill value. */
|
||||
|
||||
/* Expected output. */
|
||||
{
|
||||
{4.0f, 8.0f, 6.75f},
|
||||
{0.02f, 16.0f, 24.0f},
|
||||
}}));
|
||||
|
||||
class SummedAreaTableSumTest : public ::testing::Test {
|
||||
public:
|
||||
SummedAreaTableSumTest()
|
||||
{
|
||||
operation_ = std::make_shared<SummedAreaTableOperation>();
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
operation_->set_execution_model(eExecutionModel::FullFrame);
|
||||
operation_->set_mode(SummedAreaTableOperation::eMode::Squared);
|
||||
|
||||
area_ = rcti{0, 5, 0, 4};
|
||||
sat_ = std::make_shared<MemoryBuffer>(DataType::Color, area_);
|
||||
|
||||
const float val[4] = {1.0f, 2.0f, 1.5f, 0.1f};
|
||||
std::shared_ptr<MemoryBuffer> input = std::make_shared<MemoryBuffer>(DataType::Color, area_);
|
||||
input->fill(area_, val);
|
||||
std::shared_ptr<MemoryBuffer> offset = std::make_shared<MemoryBuffer>(
|
||||
DataType::Value, area_, true);
|
||||
offset->fill(area_, &offset_);
|
||||
|
||||
operation_->update_memory_buffer(
|
||||
sat_.get(), area_, Span<MemoryBuffer *>{input.get(), offset.get()});
|
||||
}
|
||||
|
||||
std::shared_ptr<SummedAreaTableOperation> operation_;
|
||||
std::shared_ptr<MemoryBuffer> sat_;
|
||||
rcti area_;
|
||||
float offset_ = 0.0f;
|
||||
};
|
||||
|
||||
TEST_F(SummedAreaTableSumTest, FullyInside)
|
||||
{
|
||||
rcti area;
|
||||
area.xmin = 1;
|
||||
area.xmax = 3;
|
||||
area.ymin = 1;
|
||||
area.ymax = 3;
|
||||
float4 sum = summed_area_table_sum(sat_.get(), area);
|
||||
EXPECT_EQ(sum[0], 9);
|
||||
}
|
||||
|
||||
TEST_F(SummedAreaTableSumTest, LeftEdge)
|
||||
{
|
||||
rcti area;
|
||||
area.xmin = 0;
|
||||
area.xmax = 2;
|
||||
area.ymin = 0;
|
||||
area.ymax = 2;
|
||||
float4 sum = summed_area_table_sum(sat_.get(), area);
|
||||
EXPECT_EQ(sum[0], 9);
|
||||
}
|
||||
|
||||
TEST_F(SummedAreaTableSumTest, RightEdge)
|
||||
{
|
||||
rcti area;
|
||||
area.xmin = area_.xmax - 2;
|
||||
area.xmax = area_.xmax;
|
||||
area.ymin = 0;
|
||||
area.ymax = 2;
|
||||
float4 sum = summed_area_table_sum(sat_.get(), area);
|
||||
EXPECT_EQ(sum[0], 6);
|
||||
}
|
||||
|
||||
TEST_F(SummedAreaTableSumTest, LowerRightCorner)
|
||||
{
|
||||
rcti area;
|
||||
area.xmin = area_.xmax - 1;
|
||||
area.xmax = area_.xmax;
|
||||
area.ymin = area_.ymax - 1;
|
||||
area.ymax = area_.ymax;
|
||||
float4 sum = summed_area_table_sum(sat_.get(), area);
|
||||
EXPECT_EQ(sum[0], 1);
|
||||
}
|
||||
|
||||
TEST_F(SummedAreaTableSumTest, TopLine)
|
||||
{
|
||||
rcti area;
|
||||
area.xmin = 0;
|
||||
area.xmax = 1;
|
||||
area.ymin = 0;
|
||||
area.ymax = 0;
|
||||
float4 sum = summed_area_table_sum(sat_.get(), area);
|
||||
EXPECT_EQ(sum[0], 2);
|
||||
}
|
||||
|
||||
TEST_F(SummedAreaTableSumTest, ButtomLine)
|
||||
{
|
||||
rcti area;
|
||||
area.xmin = 0;
|
||||
area.xmax = 4;
|
||||
area.ymin = 3;
|
||||
area.ymax = 3;
|
||||
float4 sum = summed_area_table_sum(sat_.get(), area);
|
||||
EXPECT_EQ(sum[0], 5);
|
||||
}
|
||||
|
||||
} // namespace blender::compositor::tests
|
|
@ -125,4 +125,4 @@ void main()
|
|||
// downsample_level(out_mip_10, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,4 +220,4 @@ ScreenTraceHitData raytrace_planar(RayTraceData rt_data,
|
|||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1227,7 +1227,7 @@ static void get_pchan_color_constraint(const ThemeWireColor *bcolor,
|
|||
else if (constflag & PCHAN_HAS_CONST) {
|
||||
constraint_color = G_draw.block.color_bone_pose_constraint;
|
||||
}
|
||||
interp_v3_v3v3(r_color, solid_color, constraint_color, 0.5f);
|
||||
interp_v4_v4v4(r_color, solid_color, constraint_color, 0.5f);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -781,6 +781,9 @@ static void workbench_render_to_image(void *vedata,
|
|||
|
||||
/* Perform render step between samples to allow
|
||||
* flushing of freed GPUBackend resources. */
|
||||
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
|
||||
GPU_flush();
|
||||
}
|
||||
GPU_render_step();
|
||||
GPU_FINISH_DELIMITER();
|
||||
} while (ved->instance->scene_state.sample + 1 < ved->instance->scene_state.samples_len);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -65,7 +65,7 @@
|
|||
|
||||
static bool get_normalized_fcurve_bounds(FCurve *fcu,
|
||||
bAnimContext *ac,
|
||||
const bAnimListElem *ale,
|
||||
bAnimListElem *ale,
|
||||
const bool include_handles,
|
||||
const float range[2],
|
||||
rctf *r_bounds)
|
||||
|
@ -93,6 +93,10 @@ static bool get_normalized_fcurve_bounds(FCurve *fcu,
|
|||
r_bounds->ymin -= (min_height - height) / 2;
|
||||
r_bounds->ymax += (min_height - height) / 2;
|
||||
}
|
||||
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
|
||||
r_bounds->xmin = BKE_nla_tweakedit_remap(adt, r_bounds->xmin, NLATIME_CONVERT_MAP);
|
||||
r_bounds->xmax = BKE_nla_tweakedit_remap(adt, r_bounds->xmax, NLATIME_CONVERT_MAP);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -156,6 +160,7 @@ static void add_region_padding(bContext *C, ARegion *region, rctf *bounds)
|
|||
UI_MARKER_MARGIN_Y;
|
||||
BLI_rctf_pad_y(bounds, region->winy, pad_bottom, pad_top);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -1268,5 +1268,5 @@ void ED_keymap_curves(wmKeyConfig *keyconf)
|
|||
using namespace blender::ed::curves;
|
||||
/* Only set in editmode curves, by space_view3d listener. */
|
||||
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
keymap->poll = editable_curves_poll;
|
||||
keymap->poll = editable_curves_in_edit_mode_poll;
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves
|
|||
if (selection_attr.domain == selection_domain) {
|
||||
return selection_attr;
|
||||
}
|
||||
selection_attr.finish();
|
||||
attributes.remove(".selection");
|
||||
}
|
||||
const int domain_size = attributes.domain_size(selection_domain);
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
@ -51,6 +52,7 @@
|
|||
#include "ED_asset_menu_utils.hh"
|
||||
#include "ED_geometry.hh"
|
||||
#include "ED_mesh.hh"
|
||||
#include "ED_sculpt.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -162,11 +164,10 @@ static bke::GeometrySet get_original_geometry_eval_copy(Object &object)
|
|||
}
|
||||
}
|
||||
|
||||
static void store_result_geometry(Main &bmain,
|
||||
Scene &scene,
|
||||
Object &object,
|
||||
bke::GeometrySet geometry)
|
||||
static void store_result_geometry(
|
||||
const wmOperator &op, Main &bmain, Scene &scene, Object &object, bke::GeometrySet geometry)
|
||||
{
|
||||
geometry.ensure_owns_direct_data();
|
||||
switch (object.type) {
|
||||
case OB_CURVES: {
|
||||
Curves &curves = *static_cast<Curves *>(object.data);
|
||||
|
@ -202,33 +203,92 @@ static void store_result_geometry(Main &bmain,
|
|||
}
|
||||
case OB_MESH: {
|
||||
Mesh &mesh = *static_cast<Mesh *>(object.data);
|
||||
|
||||
if (object.mode == OB_MODE_SCULPT) {
|
||||
ED_sculpt_undo_geometry_begin(&object, &op);
|
||||
}
|
||||
|
||||
Mesh *new_mesh = geometry.get_component_for_write<bke::MeshComponent>().release();
|
||||
if (!new_mesh) {
|
||||
BKE_mesh_clear_geometry(&mesh);
|
||||
if (object.mode == OB_MODE_EDIT) {
|
||||
EDBM_mesh_make(&object, scene.toolsettings->selectmode, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* Anonymous attributes shouldn't be available on the applied geometry. */
|
||||
new_mesh->attributes_for_write().remove_anonymous();
|
||||
|
||||
BKE_object_material_from_eval_data(&bmain, &object, &new_mesh->id);
|
||||
BKE_mesh_nomain_to_mesh(new_mesh, &mesh, &object);
|
||||
}
|
||||
|
||||
/* Anonymous attributes shouldn't be available on the applied geometry. */
|
||||
new_mesh->attributes_for_write().remove_anonymous();
|
||||
|
||||
BKE_object_material_from_eval_data(&bmain, &object, &new_mesh->id);
|
||||
BKE_mesh_nomain_to_mesh(new_mesh, &mesh, &object);
|
||||
if (object.mode == OB_MODE_EDIT) {
|
||||
EDBM_mesh_make(&object, scene.toolsettings->selectmode, true);
|
||||
BKE_editmesh_looptri_and_normals_calc(mesh.edit_mesh);
|
||||
}
|
||||
else if (object.mode == OB_MODE_SCULPT) {
|
||||
ED_sculpt_undo_geometry_end(&object);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dependency graph referencing all data-blocks used by the tree, and all selected
|
||||
* objects. Adding the selected objects is necessary because they are currently compared by pointer
|
||||
* to other evaluated objects inside of geometry nodes.
|
||||
*/
|
||||
static Depsgraph *build_depsgraph_from_indirect_ids(Main &bmain,
|
||||
Scene &scene,
|
||||
ViewLayer &view_layer,
|
||||
const bNodeTree &node_tree_orig,
|
||||
const Span<const Object *> objects,
|
||||
const IDProperty &properties)
|
||||
{
|
||||
Set<ID *> ids_for_relations;
|
||||
bool needs_own_transform_relation = false;
|
||||
nodes::find_node_tree_dependencies(
|
||||
node_tree_orig, ids_for_relations, needs_own_transform_relation);
|
||||
IDP_foreach_property(
|
||||
&const_cast<IDProperty &>(properties),
|
||||
IDP_TYPE_FILTER_ID,
|
||||
[](IDProperty *property, void *user_data) {
|
||||
if (ID *id = IDP_Id(property)) {
|
||||
static_cast<Set<ID *> *>(user_data)->add(id);
|
||||
}
|
||||
},
|
||||
&ids_for_relations);
|
||||
|
||||
Vector<const ID *> ids;
|
||||
ids.append(&node_tree_orig.id);
|
||||
ids.extend(objects.cast<const ID *>());
|
||||
ids.insert(ids.size(), ids_for_relations.begin(), ids_for_relations.end());
|
||||
|
||||
Depsgraph *depsgraph = DEG_graph_new(&bmain, &scene, &view_layer, DAG_EVAL_VIEWPORT);
|
||||
DEG_graph_build_from_ids(depsgraph, const_cast<ID **>(ids.data()), ids.size());
|
||||
return depsgraph;
|
||||
}
|
||||
|
||||
static IDProperty *replace_inputs_evaluated_data_blocks(const IDProperty &op_properties,
|
||||
const Depsgraph &depsgraph)
|
||||
{
|
||||
/* We just create a temporary copy, so don't adjust data-block user count. */
|
||||
IDProperty *properties = IDP_CopyProperty_ex(&op_properties, LIB_ID_CREATE_NO_USER_REFCOUNT);
|
||||
IDP_foreach_property(
|
||||
properties,
|
||||
IDP_TYPE_FILTER_ID,
|
||||
[](IDProperty *property, void *user_data) {
|
||||
if (ID *id = IDP_Id(property)) {
|
||||
Depsgraph *depsgraph = static_cast<Depsgraph *>(user_data);
|
||||
property->data.pointer = DEG_get_evaluated_id(depsgraph, id);
|
||||
}
|
||||
},
|
||||
&const_cast<Depsgraph &>(depsgraph));
|
||||
return properties;
|
||||
}
|
||||
|
||||
static int run_node_group_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *active_object = CTX_data_active_object(C);
|
||||
|
@ -240,11 +300,24 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
const eObjectMode mode = eObjectMode(active_object->mode);
|
||||
|
||||
const bNodeTree *node_tree = get_node_group(*C, *op->ptr, op->reports);
|
||||
if (!node_tree) {
|
||||
const bNodeTree *node_tree_orig = get_node_group(*C, *op->ptr, op->reports);
|
||||
if (!node_tree_orig) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
uint objects_len = 0;
|
||||
Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
|
||||
scene, view_layer, CTX_wm_view3d(C), &objects_len, mode);
|
||||
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(objects); });
|
||||
|
||||
Depsgraph *depsgraph = build_depsgraph_from_indirect_ids(
|
||||
*bmain, *scene, *view_layer, *node_tree_orig, {objects, objects_len}, *op->properties);
|
||||
DEG_evaluate_on_refresh(depsgraph);
|
||||
BLI_SCOPED_DEFER([&]() { DEG_graph_free(depsgraph); });
|
||||
|
||||
const bNodeTree *node_tree = reinterpret_cast<const bNodeTree *>(
|
||||
DEG_get_evaluated_id(depsgraph, const_cast<ID *>(&node_tree_orig->id)));
|
||||
|
||||
const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(*node_tree);
|
||||
if (lf_graph_info == nullptr) {
|
||||
|
@ -257,9 +330,8 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
uint objects_len = 0;
|
||||
Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
|
||||
scene, view_layer, CTX_wm_view3d(C), &objects_len, mode);
|
||||
IDProperty *properties = replace_inputs_evaluated_data_blocks(*op->properties, *depsgraph);
|
||||
BLI_SCOPED_DEFER([&]() { IDP_FreeProperty_ex(properties, false); });
|
||||
|
||||
OperatorComputeContext compute_context(op->type->idname);
|
||||
|
||||
|
@ -269,14 +341,14 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
nodes::GeoNodesOperatorData operator_eval_data{};
|
||||
operator_eval_data.depsgraph = depsgraph;
|
||||
operator_eval_data.self_object = object;
|
||||
operator_eval_data.scene = scene;
|
||||
operator_eval_data.self_object = DEG_get_evaluated_object(depsgraph, object);
|
||||
operator_eval_data.scene = DEG_get_evaluated_scene(depsgraph);
|
||||
|
||||
bke::GeometrySet geometry_orig = get_original_geometry_eval_copy(*object);
|
||||
|
||||
bke::GeometrySet new_geometry = nodes::execute_geometry_nodes_on_geometry(
|
||||
*node_tree,
|
||||
op->properties,
|
||||
properties,
|
||||
compute_context,
|
||||
std::move(geometry_orig),
|
||||
[&](nodes::GeoNodesLFUserData &user_data) {
|
||||
|
@ -284,14 +356,12 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
|
|||
user_data.log_socket_values = false;
|
||||
});
|
||||
|
||||
store_result_geometry(*bmain, *scene, *object, std::move(new_geometry));
|
||||
store_result_geometry(*op, *bmain, *scene, *object, std::move(new_geometry));
|
||||
|
||||
DEG_id_tag_update(static_cast<ID *>(object->data), ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, object->data);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(objects);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
|
|
@ -1180,7 +1180,7 @@ void create_blank(Main &bmain, Object &object, const int frame_number)
|
|||
int material_index = add_material_from_template(bmain, object, gp_stroke_material_black);
|
||||
object.actcol = material_index + 1;
|
||||
|
||||
Layer &new_layer = grease_pencil.add_layer(DATA_("GP_Layer"));
|
||||
Layer &new_layer = grease_pencil.add_layer(DATA_("Layer"));
|
||||
grease_pencil.set_active_layer(&new_layer);
|
||||
grease_pencil.insert_blank_frame(new_layer, frame_number, 0, BEZT_KEYTYPE_KEYFRAME);
|
||||
}
|
||||
|
|
|
@ -797,8 +797,13 @@ static void GREASE_PENCIL_OT_delete_frame(wmOperatorType *ot)
|
|||
"Method used for deleting Grease Pencil frames");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
static int grease_pencil_stroke_change_color_exec(bContext *C, wmOperator * /*op*/)
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stroke Material Set Operator
|
||||
* \{ */
|
||||
|
||||
static int grease_pencil_stroke_material_set_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
using namespace blender;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
@ -814,17 +819,16 @@ static int grease_pencil_stroke_change_color_exec(bContext *C, wmOperator * /*op
|
|||
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
|
||||
bke::CurvesGeometry &curves = drawing.strokes_for_write();
|
||||
|
||||
if (curves.points_num() == 0) {
|
||||
IndexMaskMemory memory;
|
||||
IndexMask selected_curves = ed::curves::retrieve_selected_curves(curves, memory);
|
||||
|
||||
if (selected_curves.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bke::SpanAttributeWriter<int> materials =
|
||||
curves.attributes_for_write().lookup_or_add_for_write_span<int>("material_index",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
IndexMask selected_curves = ed::curves::retrieve_selected_curves(curves, memory);
|
||||
|
||||
selected_curves.foreach_index(
|
||||
[&](const int curve_index) { materials.span[curve_index] = material_index; });
|
||||
|
||||
|
@ -837,13 +841,13 @@ static int grease_pencil_stroke_change_color_exec(bContext *C, wmOperator * /*op
|
|||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_stroke_change_color(wmOperatorType *ot)
|
||||
static void GREASE_PENCIL_OT_stroke_material_set(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Change Stroke color";
|
||||
ot->idname = "GREASE_PENCIL_OT_stroke_change_color";
|
||||
ot->description = "Change Stroke color with selected material";
|
||||
ot->name = "Assign Material";
|
||||
ot->idname = "GREASE_PENCIL_OT_stroke_material_set";
|
||||
ot->description = "Change Stroke material with selected material";
|
||||
|
||||
ot->exec = grease_pencil_stroke_change_color_exec;
|
||||
ot->exec = grease_pencil_stroke_material_set_exec;
|
||||
ot->poll = editable_grease_pencil_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@ -957,7 +961,7 @@ void ED_operatortypes_grease_pencil_edit()
|
|||
WM_operatortype_append(GREASE_PENCIL_OT_stroke_simplify);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_dissolve);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_delete_frame);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_stroke_change_color);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_stroke_material_set);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_cyclical_set);
|
||||
}
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ static void GREASE_PENCIL_OT_layer_reorder(wmOperatorType *ot)
|
|||
|
||||
PropertyRNA *prop = RNA_def_string(ot->srna,
|
||||
"target_layer_name",
|
||||
"GP_Layer",
|
||||
"Layer",
|
||||
INT16_MAX,
|
||||
"Target Name",
|
||||
"Name of the target layer");
|
||||
|
|
|
@ -1989,9 +1989,12 @@ bool UI_panel_can_be_pinned(const Panel *panel);
|
|||
bool UI_panel_category_is_visible(const ARegion *region);
|
||||
void UI_panel_category_add(ARegion *region, const char *name);
|
||||
PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idname);
|
||||
int UI_panel_category_index_find(ARegion *region, const char *idname);
|
||||
PanelCategoryStack *UI_panel_category_active_find(ARegion *region, const char *idname);
|
||||
const char *UI_panel_category_active_get(ARegion *region, bool set_fallback);
|
||||
void UI_panel_category_active_set(ARegion *region, const char *idname);
|
||||
/** \param index: index of item _in #ARegion.panels_category list_. */
|
||||
void UI_panel_category_index_active_set(ARegion *region, const int index);
|
||||
void UI_panel_category_active_set_default(ARegion *region, const char *idname);
|
||||
void UI_panel_category_clear_all(ARegion *region);
|
||||
/**
|
||||
|
|
|
@ -60,8 +60,13 @@ static Vector<rcti> button_section_bounds_calc(const ARegion *region, const bool
|
|||
rcti cur_section_bounds;
|
||||
BLI_rcti_init_minmax(&cur_section_bounds);
|
||||
|
||||
/* A bit annoying, but this function is called for both drawing and event handling. When
|
||||
* drawing, we need to exclude inactive blocks since they mess with the result. However, this
|
||||
* active state is only useful during drawing and must be ignored for handling (at which point
|
||||
* #uiBlock::active is false for all blocks). */
|
||||
const bool is_drawing = region->do_draw & RGN_DRAWING;
|
||||
LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) {
|
||||
if (!block->active) {
|
||||
if (is_drawing && !block->active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -2128,6 +2128,11 @@ PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idna
|
|||
BLI_findstring(®ion->panels_category, idname, offsetof(PanelCategoryDyn, idname)));
|
||||
}
|
||||
|
||||
int UI_panel_category_index_find(ARegion *region, const char *idname)
|
||||
{
|
||||
return BLI_findstringindex(®ion->panels_category, idname, offsetof(PanelCategoryDyn, idname));
|
||||
}
|
||||
|
||||
PanelCategoryStack *UI_panel_category_active_find(ARegion *region, const char *idname)
|
||||
{
|
||||
return static_cast<PanelCategoryStack *>(BLI_findstring(
|
||||
|
@ -2178,6 +2183,17 @@ void UI_panel_category_active_set(ARegion *region, const char *idname)
|
|||
ui_panel_category_active_set(region, idname, false);
|
||||
}
|
||||
|
||||
void UI_panel_category_index_active_set(ARegion *region, const int index)
|
||||
{
|
||||
PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(
|
||||
BLI_findlink(®ion->panels_category, index));
|
||||
if (!pc_dyn) {
|
||||
return;
|
||||
}
|
||||
|
||||
ui_panel_category_active_set(region, pc_dyn->idname, false);
|
||||
}
|
||||
|
||||
void UI_panel_category_active_set_default(ARegion *region, const char *idname)
|
||||
{
|
||||
if (!UI_panel_category_active_find(region, idname)) {
|
||||
|
|
|
@ -80,6 +80,7 @@ static int wm_ply_export_exec(bContext *C, wmOperator *op)
|
|||
export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv");
|
||||
export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals");
|
||||
export_params.vertex_colors = ePLYVertexColorMode(RNA_enum_get(op->ptr, "export_colors"));
|
||||
export_params.export_attributes = RNA_boolean_get(op->ptr, "export_attributes");
|
||||
export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh");
|
||||
export_params.ascii_format = RNA_boolean_get(op->ptr, "ascii_format");
|
||||
|
||||
|
@ -120,6 +121,7 @@ static void ui_ply_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
|||
uiItemR(sub, imfptr, "export_uv", UI_ITEM_NONE, IFACE_("UV Coordinates"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_normals", UI_ITEM_NONE, IFACE_("Vertex Normals"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_colors", UI_ITEM_NONE, IFACE_("Vertex Colors"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_attributes", UI_ITEM_NONE, IFACE_("Vertex Attributes"), ICON_NONE);
|
||||
uiItemR(sub,
|
||||
imfptr,
|
||||
"export_triangulated_mesh",
|
||||
|
@ -211,7 +213,11 @@ void WM_OT_ply_export(wmOperatorType *ot)
|
|||
PLY_VERTEX_COLOR_SRGB,
|
||||
"Export Vertex Colors",
|
||||
"Export vertex color attributes");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"export_attributes",
|
||||
true,
|
||||
"Export Vertex Attributes",
|
||||
"Export custom vertex attributes");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"export_triangulated_mesh",
|
||||
false,
|
||||
|
@ -243,6 +249,7 @@ static int wm_ply_import_exec(bContext *C, wmOperator *op)
|
|||
params.use_scene_unit = RNA_boolean_get(op->ptr, "use_scene_unit");
|
||||
params.global_scale = RNA_float_get(op->ptr, "global_scale");
|
||||
params.merge_verts = RNA_boolean_get(op->ptr, "merge_verts");
|
||||
params.import_attributes = RNA_boolean_get(op->ptr, "import_attributes");
|
||||
params.vertex_colors = ePLYVertexColorMode(RNA_enum_get(op->ptr, "import_colors"));
|
||||
|
||||
int files_len = RNA_collection_length(op->ptr, "files");
|
||||
|
@ -316,8 +323,10 @@ void WM_OT_ply_import(wmOperatorType *ot)
|
|||
"import_colors",
|
||||
ply_vertex_colors_mode,
|
||||
PLY_VERTEX_COLOR_SRGB,
|
||||
"Import Vertex Colors",
|
||||
"Vertex Colors",
|
||||
"Import vertex color attributes");
|
||||
RNA_def_boolean(
|
||||
ot->srna, "import_attributes", true, "Vertex Attributes", "Import custom vertex attributes");
|
||||
|
||||
/* Only show .ply files by default. */
|
||||
prop = RNA_def_string(ot->srna, "filter_glob", "*.ply", 0, "Extension Filter", "");
|
||||
|
|
|
@ -108,15 +108,15 @@ static void process_prim_path(char *prim_path)
|
|||
|
||||
/* The absolute root "/" path indicates a no-op,
|
||||
* so clear the string. */
|
||||
if (prim_path[0] == '/' && strlen(prim_path) == 1) {
|
||||
if (prim_path[0] == '/' && prim_path[1] == '\0') {
|
||||
prim_path[0] = '\0';
|
||||
}
|
||||
|
||||
/* If a prim path doesn't start with a "/" it
|
||||
* is invalid when creating the prim. */
|
||||
if (prim_path[0] != '/') {
|
||||
auto new_path = "/" + std::string(prim_path);
|
||||
snprintf(prim_path, FILE_MAX, "%s", new_path.c_str());
|
||||
const std::string prim_path_copy = std::string(prim_path);
|
||||
BLI_snprintf(prim_path, FILE_MAX, "/%s", prim_path_copy.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1716,7 +1716,7 @@ static void region_evaulate_visibility(ARegion *region)
|
|||
bool hidden = (region->flag & (RGN_FLAG_POLL_FAILED | RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) !=
|
||||
0;
|
||||
|
||||
if ((region->alignment & RGN_SPLIT_PREV) && region->prev) {
|
||||
if ((region->alignment & (RGN_SPLIT_PREV | RGN_ALIGN_HIDE_WITH_PREV)) && region->prev) {
|
||||
hidden = hidden || (region->prev->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
|
||||
}
|
||||
|
||||
|
|
|
@ -5498,7 +5498,9 @@ struct RegionAlphaInfo {
|
|||
float ED_region_blend_alpha(ARegion *region)
|
||||
{
|
||||
/* check parent too */
|
||||
if (region->regiontimer == nullptr && (region->alignment & RGN_SPLIT_PREV) && region->prev) {
|
||||
if (region->regiontimer == nullptr &&
|
||||
(region->alignment & (RGN_SPLIT_PREV | RGN_ALIGN_HIDE_WITH_PREV)) && region->prev)
|
||||
{
|
||||
region = region->prev;
|
||||
}
|
||||
|
||||
|
@ -5573,7 +5575,7 @@ void ED_region_visibility_change_update_animated(bContext *C, ScrArea *area, ARe
|
|||
}
|
||||
|
||||
if (region->next) {
|
||||
if (region->next->alignment & RGN_SPLIT_PREV) {
|
||||
if (region->next->alignment & (RGN_SPLIT_PREV | RGN_ALIGN_HIDE_WITH_PREV)) {
|
||||
rgi->child_region = region->next;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -794,8 +794,24 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
|
|||
next_face_set++;
|
||||
return next_face_set;
|
||||
}
|
||||
case PBVH_BMESH:
|
||||
return 0;
|
||||
case PBVH_BMESH: {
|
||||
const int cd_offset = CustomData_get_offset_named(
|
||||
&ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set");
|
||||
if (cd_offset == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int next_face_set = 0;
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
|
||||
const int fset = *static_cast<const int *>(POINTER_OFFSET(f->head.data, cd_offset));
|
||||
next_face_set = blender::math::max(next_face_set, fset);
|
||||
}
|
||||
|
||||
next_face_set++;
|
||||
return next_face_set;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -113,62 +113,47 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo
|
|||
|
||||
/* Draw Face Sets Brush. */
|
||||
|
||||
static void do_draw_face_sets_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
|
||||
constexpr float FACE_SET_BRUSH_MIN_FADE = 0.05f;
|
||||
|
||||
static void do_draw_face_sets_brush_faces(Object *ob, const Brush *brush, PBVHNode *node)
|
||||
{
|
||||
using namespace blender;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
const float bstrength = ss->cache->bstrength;
|
||||
|
||||
PBVHVertexIter vd;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES);
|
||||
|
||||
const float bstrength = ss->cache->bstrength;
|
||||
const int thread_id = BLI_task_parallel_thread_id(nullptr);
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
const int thread_id = BLI_task_parallel_thread_id(nullptr);
|
||||
|
||||
const Span<float3> positions(
|
||||
reinterpret_cast<const float3 *>(SCULPT_mesh_deformed_positions_get(ss)),
|
||||
SCULPT_vertex_count_get(ss));
|
||||
|
||||
AutomaskingNodeData automask_data;
|
||||
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
|
||||
SCULPT_automasking_node_update(ss, &automask_data, &vd);
|
||||
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
|
||||
for (const int face_i : ss->pmap[vd.index]) {
|
||||
const blender::IndexRange face = ss->faces[face_i];
|
||||
for (const int face_i : ss->pmap[vd.index]) {
|
||||
const blender::IndexRange face = ss->faces[face_i];
|
||||
|
||||
const float3 poly_center = bke::mesh::face_center_calc(positions,
|
||||
ss->corner_verts.slice(face));
|
||||
const float3 poly_center = bke::mesh::face_center_calc(positions,
|
||||
ss->corner_verts.slice(face));
|
||||
|
||||
if (!sculpt_brush_test_sq_fn(&test, poly_center)) {
|
||||
continue;
|
||||
}
|
||||
const bool face_hidden = ss->hide_poly && ss->hide_poly[face_i];
|
||||
if (face_hidden) {
|
||||
continue;
|
||||
}
|
||||
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
||||
brush,
|
||||
vd.co,
|
||||
sqrtf(test.dist),
|
||||
vd.no,
|
||||
vd.fno,
|
||||
vd.mask,
|
||||
vd.vertex,
|
||||
thread_id,
|
||||
&automask_data);
|
||||
|
||||
if (fade > 0.05f) {
|
||||
ss->face_sets[face_i] = ss->cache->paint_face_set;
|
||||
changed = true;
|
||||
}
|
||||
if (!sculpt_brush_test_sq_fn(&test, poly_center)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
||||
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
const bool face_hidden = ss->hide_poly && ss->hide_poly[face_i];
|
||||
if (face_hidden) {
|
||||
continue;
|
||||
}
|
||||
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
||||
|
@ -182,8 +167,8 @@ static void do_draw_face_sets_brush_task(Object *ob, const Brush *brush, PBVHNod
|
|||
thread_id,
|
||||
&automask_data);
|
||||
|
||||
if (fade > 0.05f) {
|
||||
SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set);
|
||||
if (fade > FACE_SET_BRUSH_MIN_FADE) {
|
||||
ss->face_sets[face_i] = ss->cache->paint_face_set;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -195,6 +180,162 @@ static void do_draw_face_sets_brush_task(Object *ob, const Brush *brush, PBVHNod
|
|||
}
|
||||
}
|
||||
|
||||
static void do_draw_face_sets_brush_grids(Object *ob, const Brush *brush, PBVHNode *node)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS);
|
||||
|
||||
const float bstrength = ss->cache->bstrength;
|
||||
const int thread_id = BLI_task_parallel_thread_id(nullptr);
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
|
||||
AutomaskingNodeData automask_data;
|
||||
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
|
||||
SCULPT_automasking_node_update(ss, &automask_data, &vd);
|
||||
|
||||
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
continue;
|
||||
}
|
||||
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
||||
brush,
|
||||
vd.co,
|
||||
sqrtf(test.dist),
|
||||
vd.no,
|
||||
vd.fno,
|
||||
vd.mask,
|
||||
vd.vertex,
|
||||
thread_id,
|
||||
&automask_data);
|
||||
|
||||
if (fade > FACE_SET_BRUSH_MIN_FADE) {
|
||||
SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
|
||||
if (changed) {
|
||||
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_draw_face_sets_brush_bmesh(Object *ob, const Brush *brush, PBVHNode *node)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_BMESH);
|
||||
|
||||
const float bstrength = ss->cache->bstrength;
|
||||
const int thread_id = BLI_task_parallel_thread_id(nullptr);
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
|
||||
/* Disable auto-masking code path which rely on an undo step to access original data.
|
||||
*
|
||||
* This is because the dynamic topology uses BMesh Log based undo system, which creates a single
|
||||
* node for the undo step, and its type could be different for the needs of the brush undo and
|
||||
* the original data access.
|
||||
*
|
||||
* For the brushes like Draw the ss->cache->automasking is set to nullptr at the first step of
|
||||
* the brush, as there is an explicit check there for the brushes which support dynamic topology.
|
||||
* Do it locally here for the Draw Face Set brush here, to mimic the behavior of the other
|
||||
* brushes but without marking the brush as supporting dynamic topology. */
|
||||
AutomaskingNodeData automask_data;
|
||||
SCULPT_automasking_node_begin(ob, ss, nullptr, &automask_data, node);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
const int cd_offset = CustomData_get_offset_named(
|
||||
&ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set");
|
||||
|
||||
for (BMFace *f : BKE_pbvh_bmesh_node_faces(node)) {
|
||||
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float3 face_center;
|
||||
BM_face_calc_center_median(f, face_center);
|
||||
|
||||
const BMLoop *l_iter = f->l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
if (!sculpt_brush_test_sq_fn(&test, l_iter->v->co)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BMVert *vert = l_iter->v;
|
||||
|
||||
/* There is no need to update the automasking data as it is disabled above. Additionally,
|
||||
* there is no access to the PBVHVertexIter as iteration happens over faces.
|
||||
*
|
||||
* The full auto-masking support would be very good to be implemented here, so keeping the
|
||||
* typical code flow for it here for the reference, and ease of looking at what needs to be
|
||||
* done for such integration.
|
||||
*
|
||||
* SCULPT_automasking_node_update(ss, &automask_data, &vd); */
|
||||
|
||||
const float fade = bstrength *
|
||||
SCULPT_brush_strength_factor(ss,
|
||||
brush,
|
||||
face_center,
|
||||
sqrtf(test.dist),
|
||||
f->no,
|
||||
f->no,
|
||||
0.0f,
|
||||
BKE_pbvh_make_vref(intptr_t(vert)),
|
||||
thread_id,
|
||||
&automask_data);
|
||||
|
||||
if (fade <= FACE_SET_BRUSH_MIN_FADE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int &fset = *static_cast<int *>(POINTER_OFFSET(f->head.data, cd_offset));
|
||||
fset = ss->cache->paint_face_set;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
} while ((l_iter = l_iter->next) != f->l_first);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_draw_face_sets_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
|
||||
{
|
||||
const SculptSession *ss = ob->sculpt;
|
||||
|
||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||
case PBVH_FACES:
|
||||
do_draw_face_sets_brush_faces(ob, brush, node);
|
||||
break;
|
||||
|
||||
case PBVH_GRIDS:
|
||||
do_draw_face_sets_brush_grids(ob, brush, node);
|
||||
break;
|
||||
|
||||
case PBVH_BMESH:
|
||||
do_draw_face_sets_brush_bmesh(ob, brush, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_relax_face_sets_brush_task(Object *ob,
|
||||
const Brush *brush,
|
||||
const int iteration,
|
||||
|
@ -254,12 +395,6 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nod
|
|||
SculptSession *ss = ob->sculpt;
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
|
||||
if (ss->pbvh) {
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
BKE_pbvh_face_sets_color_set(
|
||||
ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
|
||||
}
|
||||
|
||||
BKE_curvemapping_init(brush->curve);
|
||||
|
||||
TaskParallelSettings settings;
|
||||
|
@ -388,8 +523,6 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
|
|||
|
||||
if (all_visible) {
|
||||
mesh->face_sets_color_default = next_face_set;
|
||||
BKE_pbvh_face_sets_color_set(
|
||||
ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
|
||||
}
|
||||
|
||||
for (int i = 0; i < tot_vert; i++) {
|
||||
|
@ -964,7 +1097,6 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator * /*op
|
|||
max_ii(0, ss->totfaces - 1));
|
||||
mesh->face_sets_color_default = ss->face_sets[random_index];
|
||||
}
|
||||
BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
|
||||
|
||||
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(pbvh, {});
|
||||
for (PBVHNode *node : nodes) {
|
||||
|
|
|
@ -1276,6 +1276,12 @@ static int nlaedit_delete_exec(bContext *C, wmOperator * /*op*/)
|
|||
|
||||
/* if selected, delete */
|
||||
if (strip->flag & NLASTRIP_FLAG_SELECT) {
|
||||
/* Fix for #109430. Defensively exit tweak mode before deleting
|
||||
* the active strip. */
|
||||
if (ale->adt && ale->adt->actstrip == strip) {
|
||||
BKE_nla_tweakmode_exit(ale->adt);
|
||||
}
|
||||
|
||||
/* if a strip either side of this was a transition, delete those too */
|
||||
if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) {
|
||||
BKE_nlastrip_remove_and_free(&nlt->strips, strip->prev, true);
|
||||
|
|
|
@ -2110,7 +2110,8 @@ static bool node_link_is_field_link(const SpaceNode &snode, const bNodeLink &lin
|
|||
return false;
|
||||
}
|
||||
|
||||
static NodeLinkDrawConfig nodelink_get_draw_config(const View2D &v2d,
|
||||
static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNodeLink &link,
|
||||
const int th_col1,
|
||||
|
@ -2148,18 +2149,24 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const View2D &v2d,
|
|||
if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS &&
|
||||
snode.overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS)
|
||||
{
|
||||
const bNodeTree &node_tree = *snode.edittree;
|
||||
PointerRNA from_node_ptr = RNA_pointer_create(
|
||||
&const_cast<ID &>(node_tree.id), &RNA_Node, link.fromnode);
|
||||
PointerRNA to_node_ptr = RNA_pointer_create(
|
||||
&const_cast<ID &>(node_tree.id), &RNA_Node, link.tonode);
|
||||
|
||||
if (link.fromsock) {
|
||||
node_socket_color_get(*link.fromsock->typeinfo, draw_config.start_color);
|
||||
node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.start_color);
|
||||
}
|
||||
else {
|
||||
node_socket_color_get(*link.tosock->typeinfo, draw_config.start_color);
|
||||
node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.start_color);
|
||||
}
|
||||
|
||||
if (link.tosock) {
|
||||
node_socket_color_get(*link.tosock->typeinfo, draw_config.end_color);
|
||||
node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.end_color);
|
||||
}
|
||||
else {
|
||||
node_socket_color_get(*link.fromsock->typeinfo, draw_config.end_color);
|
||||
node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.end_color);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -2238,7 +2245,8 @@ static void node_draw_link_bezier_ex(const SpaceNode &snode,
|
|||
}
|
||||
}
|
||||
|
||||
void node_draw_link_bezier(const View2D &v2d,
|
||||
void node_draw_link_bezier(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNodeLink &link,
|
||||
const int th_col1,
|
||||
|
@ -2251,12 +2259,13 @@ void node_draw_link_bezier(const View2D &v2d,
|
|||
return;
|
||||
}
|
||||
const NodeLinkDrawConfig draw_config = nodelink_get_draw_config(
|
||||
v2d, snode, link, th_col1, th_col2, th_col3, selected);
|
||||
C, v2d, snode, link, th_col1, th_col2, th_col3, selected);
|
||||
|
||||
node_draw_link_bezier_ex(snode, draw_config, points);
|
||||
}
|
||||
|
||||
void node_draw_link(const View2D &v2d,
|
||||
void node_draw_link(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNodeLink &link,
|
||||
const bool selected)
|
||||
|
@ -2299,7 +2308,7 @@ void node_draw_link(const View2D &v2d,
|
|||
}
|
||||
}
|
||||
|
||||
node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3, selected);
|
||||
node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected);
|
||||
}
|
||||
|
||||
std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &snode,
|
||||
|
@ -2316,7 +2325,10 @@ std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &snode,
|
|||
return points;
|
||||
}
|
||||
|
||||
void node_draw_link_dragged(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link)
|
||||
void node_draw_link_dragged(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNodeLink &link)
|
||||
{
|
||||
if (link.fromsock == nullptr && link.tosock == nullptr) {
|
||||
return;
|
||||
|
@ -2325,7 +2337,7 @@ void node_draw_link_dragged(const View2D &v2d, const SpaceNode &snode, const bNo
|
|||
const std::array<float2, 4> points = node_link_bezier_points_dragged(snode, link);
|
||||
|
||||
const NodeLinkDrawConfig draw_config = nodelink_get_draw_config(
|
||||
v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true);
|
||||
C, v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true);
|
||||
/* End marker outline. */
|
||||
node_draw_link_end_markers(link, draw_config, points, true);
|
||||
/* Link. */
|
||||
|
|
|
@ -1090,13 +1090,16 @@ static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
|
|||
}
|
||||
}
|
||||
|
||||
static void node_draw_mute_line(const View2D &v2d, const SpaceNode &snode, const bNode &node)
|
||||
static void node_draw_mute_line(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNode &node)
|
||||
{
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
for (const bNodeLink &link : node.internal_links()) {
|
||||
if (!nodeLinkIsHidden(&link)) {
|
||||
node_draw_link_bezier(v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
|
||||
node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1195,14 +1198,21 @@ static void node_socket_outline_color_get(const bool selected,
|
|||
}
|
||||
}
|
||||
|
||||
void node_socket_color_get(const bNodeSocketType &type, float r_color[4])
|
||||
void node_socket_color_get(const bContext &C,
|
||||
const bNodeTree &ntree,
|
||||
PointerRNA &node_ptr,
|
||||
const bNodeSocket &sock,
|
||||
float r_color[4])
|
||||
{
|
||||
if (type.draw_color_simple) {
|
||||
type.draw_color_simple(&type, r_color);
|
||||
}
|
||||
else {
|
||||
if (!sock.typeinfo->draw_color) {
|
||||
copy_v4_v4(r_color, float4(1.0f, 0.0f, 1.0f, 1.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(RNA_struct_is_a(node_ptr.type, &RNA_Node));
|
||||
PointerRNA ptr = RNA_pointer_create(
|
||||
&const_cast<ID &>(ntree.id), &RNA_NodeSocket, &const_cast<bNodeSocket &>(sock));
|
||||
sock.typeinfo->draw_color((bContext *)&C, &ptr, &node_ptr, r_color);
|
||||
}
|
||||
|
||||
static void create_inspection_string_for_generic_value(const bNodeSocket &socket,
|
||||
|
@ -1634,7 +1644,9 @@ void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, ui
|
|||
MEM_freeN);
|
||||
}
|
||||
|
||||
static void node_socket_draw_nested(const bNodeTree &ntree,
|
||||
static void node_socket_draw_nested(const bContext &C,
|
||||
const bNodeTree &ntree,
|
||||
PointerRNA &node_ptr,
|
||||
uiBlock &block,
|
||||
const bNodeSocket &sock,
|
||||
const uint pos_id,
|
||||
|
@ -1649,7 +1661,7 @@ static void node_socket_draw_nested(const bNodeTree &ntree,
|
|||
|
||||
float color[4];
|
||||
float outline_color[4];
|
||||
node_socket_color_get(*sock.typeinfo, color);
|
||||
node_socket_color_get(C, ntree, node_ptr, sock, color);
|
||||
node_socket_outline_color_get(selected, sock.type, outline_color);
|
||||
|
||||
node_socket_draw(sock,
|
||||
|
@ -1844,6 +1856,7 @@ static void node_draw_shadow(const SpaceNode &snode,
|
|||
}
|
||||
|
||||
static void node_draw_sockets(const View2D &v2d,
|
||||
const bContext &C,
|
||||
const bNodeTree &ntree,
|
||||
const bNode &node,
|
||||
uiBlock &block,
|
||||
|
@ -1880,6 +1893,9 @@ static void node_draw_sockets(const View2D &v2d,
|
|||
immBeginAtMost(GPU_PRIM_POINTS, node.input_sockets().size() + node.output_sockets().size());
|
||||
}
|
||||
|
||||
PointerRNA node_ptr = RNA_pointer_create(
|
||||
&const_cast<ID &>(ntree.id), &RNA_Node, &const_cast<bNode &>(node));
|
||||
|
||||
/* Socket inputs. */
|
||||
int selected_input_len = 0;
|
||||
for (const bNodeSocket *sock : node.input_sockets()) {
|
||||
|
@ -1899,8 +1915,18 @@ static void node_draw_sockets(const View2D &v2d,
|
|||
continue;
|
||||
}
|
||||
|
||||
node_socket_draw_nested(
|
||||
ntree, block, *sock, pos_id, col_id, shape_id, size_id, outline_col_id, scale, selected);
|
||||
node_socket_draw_nested(C,
|
||||
ntree,
|
||||
node_ptr,
|
||||
block,
|
||||
*sock,
|
||||
pos_id,
|
||||
col_id,
|
||||
shape_id,
|
||||
size_id,
|
||||
outline_col_id,
|
||||
scale,
|
||||
selected);
|
||||
}
|
||||
|
||||
/* Socket outputs. */
|
||||
|
@ -1916,8 +1942,18 @@ static void node_draw_sockets(const View2D &v2d,
|
|||
continue;
|
||||
}
|
||||
|
||||
node_socket_draw_nested(
|
||||
ntree, block, *sock, pos_id, col_id, shape_id, size_id, outline_col_id, scale, selected);
|
||||
node_socket_draw_nested(C,
|
||||
ntree,
|
||||
node_ptr,
|
||||
block,
|
||||
*sock,
|
||||
pos_id,
|
||||
col_id,
|
||||
shape_id,
|
||||
size_id,
|
||||
outline_col_id,
|
||||
scale,
|
||||
selected);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1944,7 +1980,9 @@ static void node_draw_sockets(const View2D &v2d,
|
|||
continue;
|
||||
}
|
||||
if (select_all || (sock->flag & SELECT)) {
|
||||
node_socket_draw_nested(ntree,
|
||||
node_socket_draw_nested(C,
|
||||
ntree,
|
||||
node_ptr,
|
||||
block,
|
||||
*sock,
|
||||
pos_id,
|
||||
|
@ -1969,7 +2007,9 @@ static void node_draw_sockets(const View2D &v2d,
|
|||
continue;
|
||||
}
|
||||
if (select_all || (sock->flag & SELECT)) {
|
||||
node_socket_draw_nested(ntree,
|
||||
node_socket_draw_nested(C,
|
||||
ntree,
|
||||
node_ptr,
|
||||
block,
|
||||
*sock,
|
||||
pos_id,
|
||||
|
@ -2011,7 +2051,7 @@ static void node_draw_sockets(const View2D &v2d,
|
|||
|
||||
float color[4];
|
||||
float outline_color[4];
|
||||
node_socket_color_get(*socket->typeinfo, color);
|
||||
node_socket_color_get(C, ntree, node_ptr, *socket, color);
|
||||
node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color);
|
||||
|
||||
const float2 location = socket->runtime->location;
|
||||
|
@ -3023,7 +3063,7 @@ static void node_draw_basis(const bContext &C,
|
|||
|
||||
/* Wire across the node when muted/disabled. */
|
||||
if (node.flag & NODE_MUTED) {
|
||||
node_draw_mute_line(v2d, snode, node);
|
||||
node_draw_mute_line(C, v2d, snode, node);
|
||||
}
|
||||
|
||||
/* Body. */
|
||||
|
@ -3127,7 +3167,7 @@ static void node_draw_basis(const bContext &C,
|
|||
|
||||
/* Skip slow socket drawing if zoom is small. */
|
||||
if (scale > 0.2f) {
|
||||
node_draw_sockets(v2d, ntree, node, block, true, false);
|
||||
node_draw_sockets(v2d, C, ntree, node, block, true, false);
|
||||
}
|
||||
|
||||
if (is_node_panels_supported(node)) {
|
||||
|
@ -3162,7 +3202,7 @@ static void node_draw_hidden(const bContext &C,
|
|||
|
||||
/* Wire across the node when muted/disabled. */
|
||||
if (node.flag & NODE_MUTED) {
|
||||
node_draw_mute_line(v2d, snode, node);
|
||||
node_draw_mute_line(C, v2d, snode, node);
|
||||
}
|
||||
|
||||
/* Body. */
|
||||
|
@ -3312,7 +3352,7 @@ static void node_draw_hidden(const bContext &C,
|
|||
immUnbindProgram();
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
node_draw_sockets(v2d, ntree, node, block, true, false);
|
||||
node_draw_sockets(v2d, C, ntree, node, block, true, false);
|
||||
|
||||
UI_block_end(&C, &block);
|
||||
UI_block_draw(&C, &block);
|
||||
|
@ -3683,7 +3723,7 @@ static void reroute_node_draw(
|
|||
|
||||
/* Only draw input socket as they all are placed on the same position highlight
|
||||
* if node itself is selected, since we don't display the node body separately. */
|
||||
node_draw_sockets(region.v2d, ntree, node, block, false, node.flag & SELECT);
|
||||
node_draw_sockets(region.v2d, C, ntree, node, block, false, node.flag & SELECT);
|
||||
|
||||
UI_block_end(&C, &block);
|
||||
UI_block_draw(&C, &block);
|
||||
|
@ -3941,14 +3981,14 @@ static void node_draw_nodetree(const bContext &C,
|
|||
|
||||
for (const bNodeLink *link : ntree.all_links()) {
|
||||
if (!nodeLinkIsHidden(link) && !bke::nodeLinkIsSelected(link)) {
|
||||
node_draw_link(region.v2d, snode, *link, false);
|
||||
node_draw_link(C, region.v2d, snode, *link, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw selected node links after the unselected ones, so they are shown on top. */
|
||||
for (const bNodeLink *link : ntree.all_links()) {
|
||||
if (!nodeLinkIsHidden(link) && bke::nodeLinkIsSelected(link)) {
|
||||
node_draw_link(region.v2d, snode, *link, true);
|
||||
node_draw_link(C, region.v2d, snode, *link, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4199,7 +4239,7 @@ void node_draw_space(const bContext &C, ARegion ®ion)
|
|||
GPU_line_smooth(true);
|
||||
if (snode.runtime->linkdrag) {
|
||||
for (const bNodeLink &link : snode.runtime->linkdrag->links) {
|
||||
node_draw_link_dragged(v2d, snode, link);
|
||||
node_draw_link_dragged(C, v2d, snode, link);
|
||||
}
|
||||
}
|
||||
GPU_line_smooth(false);
|
||||
|
|
|
@ -165,7 +165,11 @@ int node_get_resize_cursor(NodeResizeDirection directions);
|
|||
* Usual convention here would be #node_socket_get_color(),
|
||||
* but that's already used (for setting a color property socket).
|
||||
*/
|
||||
void node_socket_color_get(const bNodeSocketType &type, float r_color[4]);
|
||||
void node_socket_color_get(const bContext &C,
|
||||
const bNodeTree &ntree,
|
||||
PointerRNA &node_ptr,
|
||||
const bNodeSocket &sock,
|
||||
float r_color[4]);
|
||||
|
||||
const char *node_socket_get_label(const bNodeSocket *socket, const char *panel_label);
|
||||
|
||||
|
@ -247,15 +251,20 @@ void nodelink_batch_end(SpaceNode &snode);
|
|||
/**
|
||||
* \note this is used for fake links in groups too.
|
||||
*/
|
||||
void node_draw_link(const View2D &v2d,
|
||||
void node_draw_link(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNodeLink &link,
|
||||
bool selected);
|
||||
void node_draw_link_dragged(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link);
|
||||
void node_draw_link_dragged(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNodeLink &link);
|
||||
/**
|
||||
* Don't do shadows if th_col3 is -1.
|
||||
*/
|
||||
void node_draw_link_bezier(const View2D &v2d,
|
||||
void node_draw_link_bezier(const bContext &C,
|
||||
const View2D &v2d,
|
||||
const SpaceNode &snode,
|
||||
const bNodeLink &link,
|
||||
int th_col1,
|
||||
|
|
|
@ -712,7 +712,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
|
|||
} // namespace blender::ed::space_node
|
||||
|
||||
void uiTemplateNodeLink(
|
||||
uiLayout *layout, bContext * /*C*/, bNodeTree *ntree, bNode *node, bNodeSocket *input)
|
||||
uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
|
||||
{
|
||||
using namespace blender::ed::space_node;
|
||||
|
||||
|
@ -726,7 +726,8 @@ void uiTemplateNodeLink(
|
|||
arg->node = node;
|
||||
arg->sock = input;
|
||||
|
||||
node_socket_color_get(*input->typeinfo, socket_col);
|
||||
PointerRNA node_ptr = RNA_pointer_create(&ntree->id, &RNA_Node, node);
|
||||
node_socket_color_get(*C, *ntree, node_ptr, *input, socket_col);
|
||||
|
||||
UI_block_layout_set_current(block, layout);
|
||||
|
||||
|
|
|
@ -2086,6 +2086,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
|
|||
draw_seq_strips(&ctx);
|
||||
sequencer_draw_retiming(C);
|
||||
draw_timeline_markers(&ctx);
|
||||
UI_view2d_view_ortho(ctx.v2d);
|
||||
ANIM_draw_previewrange(C, ctx.v2d, 1);
|
||||
draw_timeline_gizmos(&ctx);
|
||||
draw_timeline_post_view_callbacks(&ctx);
|
||||
|
|
|
@ -243,7 +243,7 @@ static SpaceLink *view3d_create(const ScrArea * /*area*/, const Scene *scene)
|
|||
region = MEM_cnew<ARegion>("asset shelf header for view3d");
|
||||
BLI_addtail(&v3d->regionbase, region);
|
||||
region->regiontype = RGN_TYPE_ASSET_SHELF_HEADER;
|
||||
region->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
|
||||
region->alignment = RGN_ALIGN_BOTTOM | RGN_ALIGN_HIDE_WITH_PREV;
|
||||
|
||||
/* tool shelf */
|
||||
region = MEM_cnew<ARegion>("toolshelf for view3d");
|
||||
|
|
|
@ -97,14 +97,26 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t)
|
|||
Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
|
||||
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
||||
|
||||
curve_populate_trans_data_structs(
|
||||
tc,
|
||||
curves,
|
||||
{} /* Currently no transform for attributes other than position. */,
|
||||
selection_per_object[i],
|
||||
use_proportional_edit,
|
||||
use_connected_only,
|
||||
0 /* No data offset for curves. */);
|
||||
std::optional<MutableSpan<float>> value_attribute;
|
||||
bke::SpanAttributeWriter<float> attribute_writer;
|
||||
if (t->mode == TFM_CURVE_SHRINKFATTEN) {
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
attribute_writer = attributes.lookup_or_add_for_write_span<float>(
|
||||
"radius",
|
||||
ATTR_DOMAIN_POINT,
|
||||
bke::AttributeInitVArray(VArray<float>::ForSingle(0.01f, curves.points_num())));
|
||||
|
||||
value_attribute = attribute_writer.span;
|
||||
}
|
||||
|
||||
curve_populate_trans_data_structs(tc,
|
||||
curves,
|
||||
value_attribute,
|
||||
selection_per_object[i],
|
||||
use_proportional_edit,
|
||||
use_connected_only,
|
||||
0 /* No data offset for curves. */);
|
||||
attribute_writer.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,6 @@ void MTLBackend::render_step()
|
|||
MTLContext::get_global_memory_manager()->get_current_safe_list();
|
||||
if (cmd_free_buffer_list->should_flush()) {
|
||||
MTLContext::get_global_memory_manager()->begin_new_safe_list();
|
||||
cmd_free_buffer_list->decrement_reference();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -418,6 +418,9 @@ class MTLBufferPool {
|
|||
std::atomic<MTLSafeFreeList *> current_free_list_;
|
||||
std::atomic<int64_t> allocations_in_pool_;
|
||||
|
||||
/* Previous list, to be released after one full frame. */
|
||||
MTLSafeFreeList *prev_free_buffer_list_ = nullptr;
|
||||
|
||||
public:
|
||||
void init(id<MTLDevice> device);
|
||||
~MTLBufferPool();
|
||||
|
|
|
@ -425,8 +425,17 @@ MTLSafeFreeList *MTLBufferPool::get_current_safe_list()
|
|||
void MTLBufferPool::begin_new_safe_list()
|
||||
{
|
||||
safelist_lock_.lock();
|
||||
MTLSafeFreeList *previous_list = prev_free_buffer_list_;
|
||||
MTLSafeFreeList *active_list = get_current_safe_list();
|
||||
current_free_list_ = new MTLSafeFreeList();
|
||||
prev_free_buffer_list_ = active_list;
|
||||
safelist_lock_.unlock();
|
||||
|
||||
/* Release final reference for previous list.
|
||||
* Note: Outside of lock as this function itself locks. */
|
||||
if (previous_list) {
|
||||
previous_list->decrement_reference();
|
||||
}
|
||||
}
|
||||
|
||||
void MTLBufferPool::ensure_buffer_pool(MTLResourceOptions options)
|
||||
|
|
|
@ -81,7 +81,7 @@ vec2 do_tria()
|
|||
int vidx = gl_VertexID % 4;
|
||||
bool tria2 = gl_VertexID > 7;
|
||||
|
||||
vec2 pos;
|
||||
vec2 pos = vec2(0.0);
|
||||
float size = (tria2) ? -tria2Size : tria1Size;
|
||||
vec2 center = (tria2) ? tria2Center : tria1Center;
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ class VKCommandBuffers : public NonCopyable, NonMovable {
|
|||
void ensure_no_compute_commands();
|
||||
|
||||
/**
|
||||
* Ensire that no draw_commands are scheduled.
|
||||
* Ensure that no draw_commands are scheduled.
|
||||
*
|
||||
* To ensure correct operation all draw commands should be flushed when adding a new compute
|
||||
* command.
|
||||
|
|
|
@ -255,8 +255,8 @@ void VKPipelineStateManager::set_stencil_test(const eGPUStencilTest test,
|
|||
switch (operation) {
|
||||
case GPU_STENCIL_OP_REPLACE:
|
||||
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_REPLACE;
|
||||
depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE;
|
||||
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.back = depth_stencil_state.front;
|
||||
break;
|
||||
|
||||
|
|
|
@ -50,7 +50,12 @@ void VKTexture::generate_mipmap()
|
|||
|
||||
VKContext &context = *VKContext::get();
|
||||
VKCommandBuffers &command_buffers = context.command_buffers_get();
|
||||
layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
command_buffers.submit();
|
||||
|
||||
layout_ensure(context,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
VK_ACCESS_TRANSFER_READ_BIT);
|
||||
|
||||
for (int src_mipmap : IndexRange(mipmaps_ - 1)) {
|
||||
int dst_mipmap = src_mipmap + 1;
|
||||
|
@ -75,7 +80,9 @@ void VKTexture::generate_mipmap()
|
|||
layout_ensure(context,
|
||||
IndexRange(src_mipmap, 1),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_TRANSFER_READ_BIT);
|
||||
|
||||
VkImageBlit image_blit = {};
|
||||
image_blit.srcOffsets[0] = {0, 0, 0};
|
||||
|
@ -97,16 +104,15 @@ void VKTexture::generate_mipmap()
|
|||
*this,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
Span<VkImageBlit>(&image_blit, 1));
|
||||
/* TODO: Until we do actual command encoding we need to submit each transfer operation
|
||||
* individually. */
|
||||
context.flush();
|
||||
}
|
||||
/* Ensure that all mipmap levels are in `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL`.
|
||||
* All MIP-levels are except the last one. */
|
||||
|
||||
/* Ensure that all mipmap levels are in `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL`. */
|
||||
layout_ensure(context,
|
||||
IndexRange(mipmaps_ - 1, 1),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_MEMORY_READ_BIT);
|
||||
current_layout_set(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
}
|
||||
|
||||
|
@ -527,7 +533,10 @@ void VKTexture::current_layout_set(const VkImageLayout new_layout)
|
|||
current_layout_ = new_layout;
|
||||
}
|
||||
|
||||
void VKTexture::layout_ensure(VKContext &context, const VkImageLayout requested_layout)
|
||||
void VKTexture::layout_ensure(VKContext &context,
|
||||
const VkImageLayout requested_layout,
|
||||
const VkAccessFlagBits src_access,
|
||||
const VkAccessFlagBits dst_access)
|
||||
{
|
||||
if (is_texture_view()) {
|
||||
source_texture_->layout_ensure(context, requested_layout);
|
||||
|
@ -537,20 +546,29 @@ void VKTexture::layout_ensure(VKContext &context, const VkImageLayout requested_
|
|||
if (current_layout == requested_layout) {
|
||||
return;
|
||||
}
|
||||
layout_ensure(context, IndexRange(0, VK_REMAINING_MIP_LEVELS), current_layout, requested_layout);
|
||||
layout_ensure(context,
|
||||
IndexRange(0, VK_REMAINING_MIP_LEVELS),
|
||||
current_layout,
|
||||
requested_layout,
|
||||
src_access,
|
||||
dst_access);
|
||||
current_layout_set(requested_layout);
|
||||
}
|
||||
|
||||
void VKTexture::layout_ensure(VKContext &context,
|
||||
const IndexRange mipmap_range,
|
||||
const VkImageLayout current_layout,
|
||||
const VkImageLayout requested_layout)
|
||||
const VkImageLayout requested_layout,
|
||||
const VkAccessFlagBits src_access,
|
||||
const VkAccessFlagBits dst_access)
|
||||
{
|
||||
BLI_assert(vk_image_ != VK_NULL_HANDLE);
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = current_layout;
|
||||
barrier.newLayout = requested_layout;
|
||||
barrier.srcAccessMask = src_access;
|
||||
barrier.dstAccessMask = dst_access;
|
||||
barrier.image = vk_image_;
|
||||
barrier.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_);
|
||||
barrier.subresourceRange.baseMipLevel = uint32_t(mipmap_range.start());
|
||||
|
|
|
@ -140,7 +140,10 @@ class VKTexture : public Texture, public VKBindableResource {
|
|||
*
|
||||
* When texture is already in the requested layout, nothing will be done.
|
||||
*/
|
||||
void layout_ensure(VKContext &context, VkImageLayout requested_layout);
|
||||
void layout_ensure(VKContext &context,
|
||||
VkImageLayout requested_layout,
|
||||
VkAccessFlagBits src_access = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
VkAccessFlagBits dst_access = VK_ACCESS_MEMORY_READ_BIT);
|
||||
|
||||
private:
|
||||
/**
|
||||
|
@ -151,7 +154,9 @@ class VKTexture : public Texture, public VKBindableResource {
|
|||
void layout_ensure(VKContext &context,
|
||||
IndexRange mipmap_range,
|
||||
VkImageLayout current_layout,
|
||||
VkImageLayout requested_layout);
|
||||
VkImageLayout requested_layout,
|
||||
VkAccessFlagBits src_access,
|
||||
VkAccessFlagBits dst_access);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ struct PLYExportParams {
|
|||
bool export_uv;
|
||||
bool export_normals;
|
||||
ePLYVertexColorMode vertex_colors;
|
||||
bool export_attributes;
|
||||
bool export_triangulated_mesh;
|
||||
};
|
||||
|
||||
|
@ -54,6 +55,7 @@ struct PLYImportParams {
|
|||
bool use_scene_unit;
|
||||
float global_scale;
|
||||
ePLYVertexColorMode vertex_colors;
|
||||
bool import_attributes;
|
||||
bool merge_verts;
|
||||
};
|
||||
|
||||
|
|
|
@ -34,6 +34,10 @@ void write_vertices(FileBuffer &buffer, const PlyData &ply_data)
|
|||
buffer.write_UV(ply_data.uv_coordinates[i].x, ply_data.uv_coordinates[i].y);
|
||||
}
|
||||
|
||||
for (const PlyCustomAttribute &attr : ply_data.vertex_custom_attr) {
|
||||
buffer.write_data(attr.data[i]);
|
||||
}
|
||||
|
||||
buffer.write_vertex_end();
|
||||
}
|
||||
buffer.write_to_file();
|
||||
|
|
|
@ -50,6 +50,10 @@ void write_header(FileBuffer &buffer,
|
|||
buffer.write_header_scalar_property("float", "t");
|
||||
}
|
||||
|
||||
for (const PlyCustomAttribute &attr : ply_data.vertex_custom_attr) {
|
||||
buffer.write_header_scalar_property("float", attr.name);
|
||||
}
|
||||
|
||||
if (!ply_data.face_sizes.is_empty()) {
|
||||
buffer.write_header_element("face", int(ply_data.face_sizes.size()));
|
||||
buffer.write_header_list_property("uchar", "uint", "vertex_indices");
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "BLI_hash.hh"
|
||||
#include "BLI_math_color.hh"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_quaternion.hh"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
@ -147,6 +148,166 @@ static void generate_vertex_map(const Mesh *mesh,
|
|||
}
|
||||
}
|
||||
|
||||
static void load_custom_attributes(const Mesh *mesh, Vector<PlyCustomAttribute> &r_attributes)
|
||||
{
|
||||
const bke::AttributeAccessor attributes = mesh->attributes();
|
||||
const StringRef color_name = mesh->active_color_attribute;
|
||||
const StringRef uv_name = CustomData_get_active_layer_name(&mesh->loop_data, CD_PROP_FLOAT2);
|
||||
|
||||
attributes.for_all([&](const bke::AttributeIDRef &attribute_id,
|
||||
const bke::AttributeMetaData &meta_data) {
|
||||
/* Skip internal, standard and non-vertex domain attributes. */
|
||||
if (meta_data.domain != ATTR_DOMAIN_POINT || attribute_id.name()[0] == '.' ||
|
||||
attribute_id.is_anonymous() || ELEM(attribute_id.name(), "position", color_name, uv_name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const GVArraySpan attribute = *mesh->attributes().lookup(
|
||||
attribute_id, meta_data.domain, meta_data.data_type);
|
||||
const int64_t size = attribute.size();
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
switch (meta_data.data_type) {
|
||||
case CD_PROP_FLOAT: {
|
||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
||||
auto typed = attribute.typed<float>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr.data[i] = typed[i];
|
||||
}
|
||||
r_attributes.append(attr);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_INT8: {
|
||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
||||
auto typed = attribute.typed<int8_t>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr.data[i] = typed[i];
|
||||
}
|
||||
r_attributes.append(attr);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_INT32: {
|
||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
||||
auto typed = attribute.typed<int32_t>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr.data[i] = typed[i];
|
||||
}
|
||||
r_attributes.append(attr);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_INT32_2D: {
|
||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
||||
auto typed = attribute.typed<int2>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr_x.data[i] = typed[i].x;
|
||||
attr_y.data[i] = typed[i].y;
|
||||
}
|
||||
r_attributes.append(attr_x);
|
||||
r_attributes.append(attr_y);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_FLOAT2: {
|
||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
||||
auto typed = attribute.typed<float2>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr_x.data[i] = typed[i].x;
|
||||
attr_y.data[i] = typed[i].y;
|
||||
}
|
||||
r_attributes.append(attr_x);
|
||||
r_attributes.append(attr_y);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_FLOAT3: {
|
||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
||||
PlyCustomAttribute attr_z(attribute_id.name() + "_z", size);
|
||||
auto typed = attribute.typed<float3>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr_x.data[i] = typed[i].x;
|
||||
attr_y.data[i] = typed[i].y;
|
||||
attr_z.data[i] = typed[i].z;
|
||||
}
|
||||
r_attributes.append(attr_x);
|
||||
r_attributes.append(attr_y);
|
||||
r_attributes.append(attr_z);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_BYTE_COLOR: {
|
||||
PlyCustomAttribute attr_r(attribute_id.name() + "_r", size);
|
||||
PlyCustomAttribute attr_g(attribute_id.name() + "_g", size);
|
||||
PlyCustomAttribute attr_b(attribute_id.name() + "_b", size);
|
||||
PlyCustomAttribute attr_a(attribute_id.name() + "_a", size);
|
||||
auto typed = attribute.typed<ColorGeometry4b>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
ColorGeometry4f col = typed[i].decode();
|
||||
attr_r.data[i] = col.r;
|
||||
attr_g.data[i] = col.g;
|
||||
attr_b.data[i] = col.b;
|
||||
attr_a.data[i] = col.a;
|
||||
}
|
||||
r_attributes.append(attr_r);
|
||||
r_attributes.append(attr_g);
|
||||
r_attributes.append(attr_b);
|
||||
r_attributes.append(attr_a);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_COLOR: {
|
||||
PlyCustomAttribute attr_r(attribute_id.name() + "_r", size);
|
||||
PlyCustomAttribute attr_g(attribute_id.name() + "_g", size);
|
||||
PlyCustomAttribute attr_b(attribute_id.name() + "_b", size);
|
||||
PlyCustomAttribute attr_a(attribute_id.name() + "_a", size);
|
||||
auto typed = attribute.typed<ColorGeometry4f>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
ColorGeometry4f col = typed[i];
|
||||
attr_r.data[i] = col.r;
|
||||
attr_g.data[i] = col.g;
|
||||
attr_b.data[i] = col.b;
|
||||
attr_a.data[i] = col.a;
|
||||
}
|
||||
r_attributes.append(attr_r);
|
||||
r_attributes.append(attr_g);
|
||||
r_attributes.append(attr_b);
|
||||
r_attributes.append(attr_a);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_BOOL: {
|
||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
||||
auto typed = attribute.typed<bool>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr.data[i] = typed[i] ? 1.0f : 0.0f;
|
||||
}
|
||||
r_attributes.append(attr);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_QUATERNION: {
|
||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
||||
PlyCustomAttribute attr_z(attribute_id.name() + "_z", size);
|
||||
PlyCustomAttribute attr_w(attribute_id.name() + "_w", size);
|
||||
auto typed = attribute.typed<math::Quaternion>();
|
||||
for (const int64_t i : typed.index_range()) {
|
||||
attr_x.data[i] = typed[i].x;
|
||||
attr_y.data[i] = typed[i].y;
|
||||
attr_z.data[i] = typed[i].z;
|
||||
attr_w.data[i] = typed[i].w;
|
||||
}
|
||||
r_attributes.append(attr_x);
|
||||
r_attributes.append(attr_y);
|
||||
r_attributes.append(attr_z);
|
||||
r_attributes.append(attr_w);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_msg(0, "Unsupported attribute type for PLY export.");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams &export_params)
|
||||
{
|
||||
DEGObjectIterSettings deg_iter_settings{};
|
||||
|
@ -267,6 +428,11 @@ void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams
|
|||
}
|
||||
}
|
||||
|
||||
/* Custom attributes */
|
||||
if (export_params.export_attributes) {
|
||||
load_custom_attributes(mesh, plyData.vertex_custom_attr);
|
||||
}
|
||||
|
||||
/* Loose edges */
|
||||
const bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
|
||||
if (loose_edges.count > 0) {
|
||||
|
|
|
@ -50,6 +50,8 @@ class FileBuffer : private NonMovable {
|
|||
|
||||
virtual void write_UV(float u, float v) = 0;
|
||||
|
||||
virtual void write_data(float v) = 0;
|
||||
|
||||
virtual void write_vertex_normal(float nx, float ny, float nz) = 0;
|
||||
|
||||
virtual void write_vertex_color(uchar r, uchar g, uchar b, uchar a) = 0;
|
||||
|
|
|
@ -20,6 +20,11 @@ void FileBufferAscii::write_UV(float u, float v)
|
|||
write_fstring(" {} {}", u, v);
|
||||
}
|
||||
|
||||
void FileBufferAscii::write_data(float v)
|
||||
{
|
||||
write_fstring(" {}", v);
|
||||
}
|
||||
|
||||
void FileBufferAscii::write_vertex_normal(float nx, float ny, float nz)
|
||||
{
|
||||
write_fstring(" {} {} {}", nx, ny, nz);
|
||||
|
|
|
@ -19,6 +19,8 @@ class FileBufferAscii : public FileBuffer {
|
|||
|
||||
void write_UV(float u, float v) override;
|
||||
|
||||
void write_data(float v) override;
|
||||
|
||||
void write_vertex_normal(float nx, float ny, float nz) override;
|
||||
|
||||
void write_vertex_color(uchar r, uchar g, uchar b, uchar a) override;
|
||||
|
|
|
@ -29,6 +29,14 @@ void FileBufferBinary::write_UV(float u, float v)
|
|||
write_bytes(span);
|
||||
}
|
||||
|
||||
void FileBufferBinary::write_data(float v)
|
||||
{
|
||||
char *bits = reinterpret_cast<char *>(&v);
|
||||
Span<char> span(bits, sizeof(float));
|
||||
|
||||
write_bytes(span);
|
||||
}
|
||||
|
||||
void FileBufferBinary::write_vertex_normal(float nx, float ny, float nz)
|
||||
{
|
||||
float3 vector(nx, ny, nz);
|
||||
|
|
|
@ -19,6 +19,8 @@ class FileBufferBinary : public FileBuffer {
|
|||
|
||||
void write_UV(float u, float v) override;
|
||||
|
||||
void write_data(float v) override;
|
||||
|
||||
void write_vertex_normal(float nx, float ny, float nz) override;
|
||||
|
||||
void write_vertex_color(uchar r, uchar g, uchar b, uchar a) override;
|
||||
|
|
|
@ -251,6 +251,19 @@ static const char *load_vertex_element(PlyReadBuffer &file,
|
|||
return "Vertex positions are not present in the file";
|
||||
}
|
||||
|
||||
Vector<int64_t> custom_attr_indices;
|
||||
for (const int64_t prop_idx : element.properties.index_range()) {
|
||||
const PlyProperty &prop = element.properties[prop_idx];
|
||||
bool is_standard = ELEM(
|
||||
prop.name, "x", "y", "z", "nx", "ny", "nz", "red", "green", "blue", "alpha", "s", "t");
|
||||
if (is_standard)
|
||||
continue;
|
||||
|
||||
custom_attr_indices.append(prop_idx);
|
||||
PlyCustomAttribute attr(prop.name, element.count);
|
||||
data->vertex_custom_attr.append(attr);
|
||||
}
|
||||
|
||||
data->vertices.reserve(element.count);
|
||||
if (has_color) {
|
||||
data->vertex_colors.reserve(element.count);
|
||||
|
@ -329,6 +342,12 @@ static const char *load_vertex_element(PlyReadBuffer &file,
|
|||
uvmap.y = value_vec[uv_index.y];
|
||||
data->uv_coordinates.append(uvmap);
|
||||
}
|
||||
|
||||
/* Custom attributes */
|
||||
for (const int64_t ci : custom_attr_indices.index_range()) {
|
||||
float value = value_vec[custom_attr_indices[ci]];
|
||||
data->vertex_custom_attr[ci].data[i] = value;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -100,6 +100,15 @@ Mesh *convert_ply_to_mesh(PlyData &data, const PLYImportParams ¶ms)
|
|||
uv_map.finish();
|
||||
}
|
||||
|
||||
/* Custom attributes */
|
||||
if (params.import_attributes && !data.vertex_custom_attr.is_empty()) {
|
||||
for (const PlyCustomAttribute &attr : data.vertex_custom_attr) {
|
||||
attributes.add<float>(attr.name,
|
||||
ATTR_DOMAIN_POINT,
|
||||
bke::AttributeInitVArray(VArray<float>::ForSpan(attr.data)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate edges from the rest of the mesh. */
|
||||
BKE_mesh_calc_edges(mesh, true, false);
|
||||
|
||||
|
|
|
@ -8,17 +8,26 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::io::ply {
|
||||
|
||||
enum PlyDataTypes { NONE, CHAR, UCHAR, SHORT, USHORT, INT, UINT, FLOAT, DOUBLE, PLY_TYPE_COUNT };
|
||||
|
||||
struct PlyCustomAttribute {
|
||||
PlyCustomAttribute(const StringRef name_, int64_t size) : name(name_), data(size) {}
|
||||
std::string name;
|
||||
Array<float> data; /* Any custom PLY attributes are converted to floats. */
|
||||
};
|
||||
|
||||
struct PlyData {
|
||||
Vector<float3> vertices;
|
||||
Vector<float3> vertex_normals;
|
||||
Vector<float4> vertex_colors; /* Linear space, 0..1 range colors. */
|
||||
Vector<PlyCustomAttribute> vertex_custom_attr;
|
||||
Vector<std::pair<int, int>> edges;
|
||||
Vector<uint32_t> face_vertices;
|
||||
Vector<uint32_t> face_sizes;
|
||||
|
|
|
@ -607,17 +607,16 @@ CacheReader *CacheReader_open_usd_object(CacheArchiveHandle *handle,
|
|||
return reader;
|
||||
}
|
||||
|
||||
if (reader) {
|
||||
USD_CacheReader_free(reader);
|
||||
}
|
||||
|
||||
pxr::UsdPrim prim = archive->stage()->GetPrimAtPath(pxr::SdfPath(object_path));
|
||||
|
||||
if (!prim) {
|
||||
WM_reportf(RPT_WARNING, "USD Import: unable to open cache reader for object %s", object_path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (reader) {
|
||||
USD_CacheReader_free(reader);
|
||||
}
|
||||
|
||||
/* TODO(makowalski): The handle does not have the proper import params or settings. */
|
||||
USDPrimReader *usd_reader = archive->create_reader(prim);
|
||||
|
||||
|
|
|
@ -681,10 +681,16 @@ enum {
|
|||
/* Maximum 15. */
|
||||
|
||||
/* Flags start here. */
|
||||
/** Region is split into the previous one, they share the same space along a common edge.
|
||||
* Includes the #RGN_ALIGN_HIDE_WITH_PREV behavior. */
|
||||
RGN_SPLIT_PREV = 1 << 5,
|
||||
/** Always let scaling this region scale the previous region instead. Useful to let regions
|
||||
* appear like they are one (while having independent layout, scrolling, etc.). */
|
||||
RGN_SPLIT_SCALE_PREV = 1 << 6,
|
||||
/** Whenever the previous region is hidden, this region becomes invisible too. #RGN_FLAG_HIDDEN
|
||||
* should only be set for the previous region, not this. The evaluated visibility respecting this
|
||||
* flag can be queried via #ARegion.visible */
|
||||
RGN_ALIGN_HIDE_WITH_PREV = 1 << 7,
|
||||
};
|
||||
|
||||
/** Mask out flags so we can check the alignment. */
|
||||
|
|
|
@ -40,8 +40,15 @@ const EnumPropertyItem rna_enum_region_type_items[] = {
|
|||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem rna_enum_region_panel_category_items[] = {
|
||||
{-1, "UNSUPPORTED", 0, "Not Supported", "This region does not support panel categories"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
|
@ -57,6 +64,8 @@ const EnumPropertyItem rna_enum_region_type_items[] = {
|
|||
|
||||
# include "UI_view2d.hh"
|
||||
|
||||
# include "BLT_translation.h"
|
||||
|
||||
# ifdef WITH_PYTHON
|
||||
# include "BPY_extern.h"
|
||||
# endif
|
||||
|
@ -290,6 +299,61 @@ static PointerRNA rna_Region_data_get(PointerRNA *ptr)
|
|||
return PointerRNA_NULL;
|
||||
}
|
||||
|
||||
static int rna_region_active_panel_category_editable_get(PointerRNA *ptr, const char **r_info)
|
||||
{
|
||||
ARegion *region = static_cast<ARegion *>(ptr->data);
|
||||
if (BLI_listbase_is_empty(®ion->panels_category)) {
|
||||
if (r_info) {
|
||||
*r_info = N_("This region does not support panel categories");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return PROP_EDITABLE;
|
||||
}
|
||||
|
||||
static int rna_region_active_panel_category_get(PointerRNA *ptr)
|
||||
{
|
||||
ARegion *region = static_cast<ARegion *>(ptr->data);
|
||||
const char *idname = UI_panel_category_active_get(region, false);
|
||||
return UI_panel_category_index_find(region, idname);
|
||||
}
|
||||
|
||||
static void rna_region_active_panel_category_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
BLI_assert(rna_region_active_panel_category_editable_get(ptr, nullptr));
|
||||
|
||||
ARegion *region = static_cast<ARegion *>(ptr->data);
|
||||
UI_panel_category_index_active_set(region, value);
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_region_active_panel_category_itemf(bContext * /*C*/,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
ARegion *region = static_cast<ARegion *>(ptr->data);
|
||||
|
||||
if (!rna_region_active_panel_category_editable_get(ptr, nullptr)) {
|
||||
*r_free = false;
|
||||
return rna_enum_region_panel_category_items;
|
||||
}
|
||||
|
||||
EnumPropertyItem *items = nullptr;
|
||||
EnumPropertyItem item = {0, "", 0, "", ""};
|
||||
int totitems = 0;
|
||||
int category_index;
|
||||
LISTBASE_FOREACH_INDEX (PanelCategoryDyn *, pc_dyn, ®ion->panels_category, category_index) {
|
||||
item.value = category_index;
|
||||
item.identifier = pc_dyn->idname;
|
||||
item.name = pc_dyn->idname;
|
||||
RNA_enum_item_add(&items, &totitems, &item);
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&items, &totitems);
|
||||
*r_free = true;
|
||||
return items;
|
||||
}
|
||||
|
||||
static void rna_View2D_region_to_view(View2D *v2d, float x, float y, float result[2])
|
||||
{
|
||||
UI_view2d_region_to_view(v2d, x, y, &result[0], &result[1]);
|
||||
|
@ -564,6 +628,20 @@ static void rna_def_region(BlenderRNA *brna)
|
|||
RNA_def_property_struct_type(prop, "AnyType");
|
||||
RNA_def_property_pointer_funcs(prop, "rna_Region_data_get", nullptr, nullptr, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "active_panel_category", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_editable_func(prop, "rna_region_active_panel_category_editable_get");
|
||||
RNA_def_property_enum_items(prop, rna_enum_region_panel_category_items);
|
||||
RNA_def_property_enum_funcs(prop,
|
||||
"rna_region_active_panel_category_get",
|
||||
"rna_region_active_panel_category_set",
|
||||
"rna_region_active_panel_category_itemf");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Active Panel Category",
|
||||
"The current active panel category, may be Null if the region does not "
|
||||
"support this feature (NOTE: these categories are generated at runtime, so list may be "
|
||||
"empty at initialization, before any drawing took place)");
|
||||
|
||||
RNA_def_function(srna, "tag_redraw", "ED_region_tag_redraw");
|
||||
}
|
||||
|
||||
|
|
|
@ -935,19 +935,6 @@ static void rna_Sequence_audio_update(Main * /*bmain*/, Scene * /*scene*/, Point
|
|||
DEG_id_tag_update(ptr->owner_id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO);
|
||||
}
|
||||
|
||||
static void rna_Sequence_speed_factor_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
SEQ_cache_cleanup(scene);
|
||||
rna_Sequence_audio_update(bmain, scene, ptr);
|
||||
}
|
||||
|
||||
static void rna_Sequence_speed_factor_set(PointerRNA *ptr, float value)
|
||||
{
|
||||
Sequence *seq = (Sequence *)ptr->data;
|
||||
Scene *scene = (Scene *)ptr->owner_id;
|
||||
SEQ_time_speed_factor_set(scene, seq, value);
|
||||
}
|
||||
|
||||
static void rna_Sequence_pan_range(
|
||||
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
|
||||
{
|
||||
|
@ -2554,19 +2541,6 @@ static void rna_def_editor(BlenderRNA *brna)
|
|||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
}
|
||||
|
||||
static void rna_def_speed_factor(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop = RNA_def_property(srna, "speed_factor", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "speed_factor");
|
||||
RNA_def_property_float_default(prop, 1.0f);
|
||||
RNA_def_property_range(prop, 0.1f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 1.0f, 100.0f, 10.0, 3);
|
||||
RNA_def_property_ui_text(prop, "Speed Factor", "Multiply playback speed");
|
||||
RNA_def_property_float_funcs(
|
||||
prop, nullptr, "rna_Sequence_speed_factor_set", nullptr); /* overlap test */
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_speed_factor_update");
|
||||
}
|
||||
|
||||
static void rna_def_filter_video(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
@ -3071,7 +3045,6 @@ static void rna_def_sound(BlenderRNA *brna)
|
|||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, nullptr);
|
||||
|
||||
rna_def_input(srna);
|
||||
rna_def_speed_factor(srna);
|
||||
}
|
||||
|
||||
static void rna_def_effect(BlenderRNA *brna)
|
||||
|
|
|
@ -112,98 +112,6 @@ static void init_data(ModifierData *md)
|
|||
nmd->runtime->cache = std::make_shared<bake::ModifierCache>();
|
||||
}
|
||||
|
||||
static void add_used_ids_from_sockets(const ListBase &sockets, Set<ID *> &ids)
|
||||
{
|
||||
LISTBASE_FOREACH (const bNodeSocket *, socket, &sockets) {
|
||||
switch (socket->type) {
|
||||
case SOCK_OBJECT: {
|
||||
if (Object *object = ((bNodeSocketValueObject *)socket->default_value)->value) {
|
||||
ids.add(&object->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_COLLECTION: {
|
||||
if (Collection *collection = ((bNodeSocketValueCollection *)socket->default_value)->value)
|
||||
{
|
||||
ids.add(&collection->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_MATERIAL: {
|
||||
if (Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value) {
|
||||
ids.add(&material->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_TEXTURE: {
|
||||
if (Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value) {
|
||||
ids.add(&texture->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_IMAGE: {
|
||||
if (Image *image = ((bNodeSocketValueImage *)socket->default_value)->value) {
|
||||
ids.add(&image->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \note We can only check properties here that cause the dependency graph to update relations when
|
||||
* they are changed, otherwise there may be a missing relation after editing. So this could check
|
||||
* more properties like whether the node is muted, but we would have to accept the cost of updating
|
||||
* relations when those properties are changed.
|
||||
*/
|
||||
static bool node_needs_own_transform_relation(const bNode &node)
|
||||
{
|
||||
if (node.type == GEO_NODE_COLLECTION_INFO) {
|
||||
const NodeGeometryCollectionInfo &storage = *static_cast<const NodeGeometryCollectionInfo *>(
|
||||
node.storage);
|
||||
return storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE;
|
||||
}
|
||||
|
||||
if (node.type == GEO_NODE_OBJECT_INFO) {
|
||||
const NodeGeometryObjectInfo &storage = *static_cast<const NodeGeometryObjectInfo *>(
|
||||
node.storage);
|
||||
return storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE;
|
||||
}
|
||||
|
||||
if (node.type == GEO_NODE_SELF_OBJECT) {
|
||||
return true;
|
||||
}
|
||||
if (node.type == GEO_NODE_DEFORM_CURVES_ON_SURFACE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void process_nodes_for_depsgraph(const bNodeTree &tree,
|
||||
Set<ID *> &ids,
|
||||
bool &r_needs_own_transform_relation,
|
||||
Set<const bNodeTree *> &checked_groups)
|
||||
{
|
||||
if (!checked_groups.add(&tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tree.ensure_topology_cache();
|
||||
for (const bNode *node : tree.all_nodes()) {
|
||||
add_used_ids_from_sockets(node->inputs, ids);
|
||||
add_used_ids_from_sockets(node->outputs, ids);
|
||||
r_needs_own_transform_relation |= node_needs_own_transform_relation(*node);
|
||||
}
|
||||
|
||||
for (const bNode *node : tree.group_nodes()) {
|
||||
if (const bNodeTree *sub_tree = reinterpret_cast<const bNodeTree *>(node->id)) {
|
||||
process_nodes_for_depsgraph(*sub_tree, ids, r_needs_own_transform_relation, checked_groups);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void find_used_ids_from_settings(const NodesModifierSettings &settings, Set<ID *> &ids)
|
||||
{
|
||||
IDP_foreach_property(
|
||||
|
@ -259,9 +167,7 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont
|
|||
bool needs_own_transform_relation = false;
|
||||
Set<ID *> used_ids;
|
||||
find_used_ids_from_settings(nmd->settings, used_ids);
|
||||
Set<const bNodeTree *> checked_groups;
|
||||
process_nodes_for_depsgraph(
|
||||
*nmd->node_group, used_ids, needs_own_transform_relation, checked_groups);
|
||||
nodes::find_node_tree_dependencies(*nmd->node_group, used_ids, needs_own_transform_relation);
|
||||
|
||||
if (ctx->object->type == OB_CURVES) {
|
||||
Curves *curves_id = static_cast<Curves *>(ctx->object->data);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "BLI_compute_context.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_multi_value_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_node.h"
|
||||
|
@ -29,6 +30,10 @@ class GeoModifierLog;
|
|||
|
||||
namespace blender::nodes {
|
||||
|
||||
void find_node_tree_dependencies(const bNodeTree &tree,
|
||||
Set<ID *> &r_ids,
|
||||
bool &r_needs_own_transform_relation);
|
||||
|
||||
StringRef input_use_attribute_suffix();
|
||||
StringRef input_attribute_name_suffix();
|
||||
|
||||
|
|
|
@ -257,9 +257,7 @@ class IndexOnSplineFieldInput final : public bke::CurvesFieldInput {
|
|||
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;
|
||||
}
|
||||
array_utils::fill_index_range(indices);
|
||||
}
|
||||
});
|
||||
return VArray<int>::ForContainer(std::move(result));
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue