WIP: Vulkan: Workbench #107886
|
@ -90,11 +90,18 @@ set(USD_EXTRA_ARGS
|
|||
-DTBB_LIBRARIES=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
|
||||
-DTbb_TBB_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
|
||||
-DTBB_tbb_LIBRARY_RELEASE=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
|
||||
# USD wants the tbb debug lib set even when you are doing a release build
|
||||
# Otherwise it will error out during the cmake configure phase.
|
||||
-DTBB_LIBRARIES_DEBUG=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
|
||||
)
|
||||
|
||||
# Ray: I'm not sure if the other platforms relied on this or not but this is no longer
|
||||
# needed for windows. If mac/lin confirm, this can be removed.
|
||||
if(NOT WIN32)
|
||||
LIST(append USD_EXTRA_ARGS
|
||||
# USD wants the tbb debug lib set even when you are doing a release build
|
||||
# Otherwise it will error out during the cmake configure phase.
|
||||
-DTBB_LIBRARIES_DEBUG=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
|
||||
)
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(external_usd
|
||||
URL file://${PACKAGE_DIR}/${USD_FILE}
|
||||
DOWNLOAD_DIR ${DOWNLOAD_DIR}
|
||||
|
|
|
@ -104,12 +104,12 @@ if %ERRORLEVEL% NEQ 0 (
|
|||
|
||||
set StatusFile=%BUILD_DIR%\%1_%2.log
|
||||
set original_path=%path%
|
||||
set oiio_paths=%Staging%\%BuildDir%%ARCH%R\Release\openimageio\bin;%Staging%\%BuildDir%%ARCH%D\Debug\openimageio\bin
|
||||
set boost_paths=%Staging%\%BuildDir%%ARCH%R\Release\boost\lib;%Staging%\%BuildDir%%ARCH%D\Debug\boost\lib
|
||||
set openexr_paths=%Staging%\%BuildDir%%ARCH%R\Release\openexr\bin;%Staging%\%BuildDir%%ARCH%D\Debug\openexr\bin
|
||||
set imath_paths=%Staging%\%BuildDir%%ARCH%R\Release\imath\bin;%Staging%\%BuildDir%%ARCH%D\Debug\imath\bin
|
||||
set tbb_paths=%Staging%\%BuildDir%%ARCH%R\Release\tbb\bin;%Staging%\%BuildDir%%ARCH%D\Debug\tbb\bin
|
||||
set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads\nasm-2.12.01\;%path%;%boost_paths%;%oiio_paths%;%openexr_paths%;%imath_paths%;%tbb_paths%
|
||||
set oiio_paths=%Staging%\%BuildDir%%ARCH%R\Release\openimageio\bin
|
||||
set boost_paths=%Staging%\%BuildDir%%ARCH%R\Release\boost\lib
|
||||
set openexr_paths=%Staging%\%BuildDir%%ARCH%R\Release\openexr\bin
|
||||
set imath_paths=%Staging%\%BuildDir%%ARCH%R\Release\imath\bin
|
||||
set tbb_paths=%Staging%\%BuildDir%%ARCH%R\Release\tbb\bin
|
||||
set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads\nasm-2.12.01\;%original_path%;%boost_paths%;%oiio_paths%;%openexr_paths%;%imath_paths%;%tbb_paths%
|
||||
mkdir %STAGING%\%BuildDir%%ARCH%R
|
||||
cd %Staging%\%BuildDir%%ARCH%R
|
||||
echo %DATE% %TIME% : Start > %StatusFile%
|
||||
|
@ -125,6 +125,12 @@ if "%NODEBUG%" == "1" goto exit
|
|||
cd %BUILD_DIR%
|
||||
mkdir %STAGING%\%BuildDir%%ARCH%D
|
||||
cd %Staging%\%BuildDir%%ARCH%D
|
||||
set oiio_paths=%Staging%\%BuildDir%%ARCH%D\Debug\openimageio\bin
|
||||
set boost_paths=%Staging%\%BuildDir%%ARCH%D\Debug\boost\lib
|
||||
set openexr_paths=%Staging%\%BuildDir%%ARCH%D\Debug\openexr\bin
|
||||
set imath_paths=%Staging%\%BuildDir%%ARCH%D\Debug\imath\bin
|
||||
set tbb_paths=%Staging%\%BuildDir%%ARCH%D\Debug\tbb\bin
|
||||
set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads\nasm-2.12.01\;%original_path%;%boost_paths%;%oiio_paths%;%openexr_paths%;%imath_paths%;%tbb_paths%
|
||||
cmake -G "%CMAKE_BUILDER%" %CMAKE_BUILD_ARCH% -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DCMAKE_BUILD_TYPE=Debug -DBUILD_MODE=Debug -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ %CMAKE_DEBUG_OPTIONS%
|
||||
echo %DATE% %TIME% : Debug Configuration done >> %StatusFile%
|
||||
if "%dobuild%" == "1" (
|
||||
|
|
|
@ -30,7 +30,7 @@ int blender_device_threads(BL::Scene &b_scene)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void adjust_device_info_from_preferences(DeviceInfo &info, PointerRNA cpreferences)
|
||||
void static adjust_device_info_from_preferences(DeviceInfo &info, PointerRNA cpreferences)
|
||||
{
|
||||
if (!get_boolean(cpreferences, "peer_memory")) {
|
||||
info.has_peer_memory = false;
|
||||
|
|
|
@ -610,6 +610,9 @@ def km_window(params):
|
|||
("wm.open_mainfile", {"type": 'O', "value": 'PRESS', "ctrl": True}, None),
|
||||
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True}, None),
|
||||
("wm.save_as_mainfile", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True}, None),
|
||||
("wm.save_mainfile",
|
||||
{"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True},
|
||||
{"properties": [("incremental", True)]}),
|
||||
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
|
||||
|
||||
# Quick menu and toolbar
|
||||
|
@ -2521,7 +2524,7 @@ def km_nla_generic(_params):
|
|||
sidebar_key={"type": 'N', "value": 'PRESS'},
|
||||
),
|
||||
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'},
|
||||
{"properties": [("use_upper_stack_evaluation", False)]}),
|
||||
{"properties": [("use_upper_stack_evaluation", True)]}),
|
||||
("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS'}, None),
|
||||
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("isolate_action", True)]}),
|
||||
|
@ -4510,6 +4513,12 @@ def km_face_mask(params):
|
|||
{"properties": [("deselect", True)]}),
|
||||
("paint.face_select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None),
|
||||
("paint.face_select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
|
||||
("paint.face_select_loop", {"type": params.select_mouse, "value": 'PRESS', "alt": True},
|
||||
{"properties": [('extend', False), ('select', True)]}),
|
||||
("paint.face_select_loop", {"type": params.select_mouse, "value": 'PRESS', "alt": True, "shift": True},
|
||||
{"properties": [('extend', True), ('select', True)]}),
|
||||
("paint.face_select_loop", {"type": params.select_mouse, "value": 'PRESS', "alt": True, "shift": True, "ctrl": True},
|
||||
{"properties": [('extend', True), ('select', False)]}),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -4591,7 +4600,7 @@ def km_pose(params):
|
|||
("armature.layers_show_all", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, None),
|
||||
("armature.armature_layers", {"type": 'M', "value": 'PRESS', "shift": True}, None),
|
||||
("pose.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
|
||||
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None),
|
||||
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
|
||||
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
|
||||
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
|
||||
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
|
||||
|
@ -5110,8 +5119,6 @@ def km_weight_paint(params):
|
|||
("paint.weight_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
|
||||
("paint.weight_sample", {"type": params.action_mouse, "value": 'PRESS', "ctrl": True}, None),
|
||||
("paint.weight_sample_group", {"type": params.action_mouse, "value": 'PRESS', "shift": True}, None),
|
||||
("paint.weight_gradient", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("type", 'LINEAR')]}),
|
||||
("paint.weight_gradient", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
|
||||
{"properties": [("type", 'RADIAL')]}),
|
||||
("paint.weight_set", {"type": 'K', "value": 'PRESS', "shift": True}, None),
|
||||
|
@ -5522,7 +5529,7 @@ def km_armature(params):
|
|||
("armature.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
|
||||
# Special transforms.
|
||||
op_tool_optional(
|
||||
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None),
|
||||
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
|
||||
(op_tool_cycle, "builtin.bone_size"), params),
|
||||
op_tool_optional(
|
||||
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},
|
||||
|
|
|
@ -188,6 +188,9 @@ def km_window(params):
|
|||
("wm.open_mainfile", {"type": 'O', "value": 'PRESS', "ctrl": True}, None),
|
||||
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True}, None),
|
||||
("wm.save_as_mainfile", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True}, None),
|
||||
("wm.save_mainfile",
|
||||
{"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True},
|
||||
{"properties": [("incremental", True)]}),
|
||||
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
|
||||
|
||||
# Quick menu and toolbar
|
||||
|
|
|
@ -224,6 +224,16 @@ class TEXT_MT_view(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
props = layout.operator("wm.context_cycle_int", text="Zoom In")
|
||||
props.data_path = "space_data.font_size"
|
||||
props.reverse = False
|
||||
|
||||
props = layout.operator("wm.context_cycle_int", text="Zoom Out")
|
||||
props.data_path = "space_data.font_size"
|
||||
props.reverse = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("TEXT_MT_view_navigation")
|
||||
|
||||
layout.separator()
|
||||
|
|
|
@ -283,6 +283,10 @@ class TOPBAR_MT_file(Menu):
|
|||
layout.operator_context = 'EXEC_AREA' if context.blend_data.is_saved else 'INVOKE_AREA'
|
||||
layout.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
|
||||
|
||||
sub = layout.row()
|
||||
sub.enabled = context.blend_data.is_saved
|
||||
sub.operator("wm.save_mainfile", text="Save Incremental").incremental = True
|
||||
|
||||
layout.operator_context = 'INVOKE_AREA'
|
||||
layout.operator("wm.save_as_mainfile", text="Save As...")
|
||||
layout.operator_context = 'INVOKE_AREA'
|
||||
|
|
|
@ -2311,6 +2311,7 @@ class VIEW3D_MT_grease_pencil_add(Menu):
|
|||
layout = self.layout
|
||||
layout.operator("object.grease_pencil_add", text="Empty", icon='EMPTY_AXIS').type = 'EMPTY'
|
||||
layout.operator("object.grease_pencil_add", text="Stroke", icon='STROKE').type = 'STROKE'
|
||||
layout.operator("object.grease_pencil_add", text="Suzanne", icon='MONKEY').type = 'MONKEY'
|
||||
|
||||
|
||||
class VIEW3D_MT_add(Menu):
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "BLI_generic_span.hh"
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
|
@ -928,6 +929,20 @@ void gather_attributes(AttributeAccessor src_attributes,
|
|||
const IndexMask &selection,
|
||||
MutableAttributeAccessor dst_attributes);
|
||||
|
||||
/**
|
||||
* Copy attribute values from groups groups defined by \a src_offsets to groups defined by \a
|
||||
* dst_offsets. The group indices are gathered to the result by \a selection. The size of each
|
||||
* source and result group must be the same.
|
||||
*/
|
||||
void gather_attributes_group_to_group(AttributeAccessor src_attributes,
|
||||
eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip,
|
||||
OffsetIndices<int> src_offsets,
|
||||
OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
MutableAttributeAccessor dst_attributes);
|
||||
|
||||
void copy_attributes(const AttributeAccessor src_attributes,
|
||||
const eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
|
|
|
@ -174,20 +174,6 @@ void ramp_blend(int type, float r_col[3], float fac, const float col[3]);
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Copy/Paste
|
||||
* \{ */
|
||||
|
||||
void BKE_material_copybuf_clear(void);
|
||||
void BKE_material_copybuf_free(void);
|
||||
void BKE_material_copybuf_copy(struct Main *bmain, struct Material *ma);
|
||||
/**
|
||||
* \return true when the material was modified.
|
||||
*/
|
||||
bool BKE_material_copybuf_paste(struct Main *bmain, struct Material *ma);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Default Materials
|
||||
* \{ */
|
||||
|
|
|
@ -137,14 +137,13 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Reduce user-count for current action. */
|
||||
/* Unassign current action. */
|
||||
if (adt->action) {
|
||||
id_us_min((ID *)adt->action);
|
||||
adt->action = NULL;
|
||||
}
|
||||
|
||||
if (act == NULL) {
|
||||
/* Just clearing the action. */
|
||||
adt->action = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1028,6 +1028,58 @@ void gather_attributes(const AttributeAccessor src_attributes,
|
|||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
const Span<T> src,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||
dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i]));
|
||||
});
|
||||
}
|
||||
|
||||
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
const GSpan src,
|
||||
GMutableSpan dst)
|
||||
{
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
gather_group_to_group(src_offsets, dst_offsets, selection, src.typed<T>(), dst.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
void gather_attributes_group_to_group(const AttributeAccessor src_attributes,
|
||||
const eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip,
|
||||
const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
MutableAttributeAccessor dst_attributes)
|
||||
{
|
||||
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (meta_data.domain != domain) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
if (skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
const GVArraySpan src = *src_attributes.lookup(id, domain);
|
||||
bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
|
||||
id, domain, meta_data.data_type);
|
||||
gather_group_to_group(src_offsets, dst_offsets, selection, src, dst.span);
|
||||
dst.finish();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void copy_attributes(const AttributeAccessor src_attributes,
|
||||
const eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
|
|
|
@ -970,18 +970,32 @@ void BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData *workspace
|
|||
/** \name Blend File Write (Partial)
|
||||
* \{ */
|
||||
|
||||
void BKE_blendfile_write_partial_begin(Main *bmain_src)
|
||||
static void blendfile_write_partial_clear_flags(Main *bmain_src)
|
||||
{
|
||||
BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false);
|
||||
ListBase *lbarray[INDEX_ID_MAX];
|
||||
int a = set_listbasepointers(bmain_src, lbarray);
|
||||
while (a--) {
|
||||
LISTBASE_FOREACH (ID *, id, lbarray[a]) {
|
||||
id->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
|
||||
id->flag &= ~(LIB_CLIPBOARD_MARK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_blendfile_write_partial_begin(Main *bmain)
|
||||
{
|
||||
blendfile_write_partial_clear_flags(bmain);
|
||||
}
|
||||
|
||||
void BKE_blendfile_write_partial_tag_ID(ID *id, bool set)
|
||||
{
|
||||
if (set) {
|
||||
id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
|
||||
id->flag |= LIB_CLIPBOARD_MARK;
|
||||
}
|
||||
else {
|
||||
id->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
|
||||
id->flag &= ~LIB_CLIPBOARD_MARK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1080,7 +1094,7 @@ bool BKE_blendfile_write_partial(Main *bmain_src,
|
|||
|
||||
void BKE_blendfile_write_partial_end(Main *bmain_src)
|
||||
{
|
||||
BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false);
|
||||
blendfile_write_partial_clear_flags(bmain_src);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -1176,62 +1176,28 @@ void CurvesGeometry::remove_points(const IndexMask &points_to_delete,
|
|||
*this = curves_copy_point_selection(*this, points_to_copy, propagation_info);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
const Span<T> src,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
selection.foreach_index(GrainSize(256), [&](const int64_t src_i, const int64_t dst_i) {
|
||||
dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i]));
|
||||
});
|
||||
}
|
||||
|
||||
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
const GSpan src,
|
||||
GMutableSpan dst)
|
||||
{
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
gather_group_to_group(src_offsets, dst_offsets, selection, src.typed<T>(), dst.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
CurvesGeometry curves_copy_curve_selection(
|
||||
const CurvesGeometry &curves,
|
||||
const IndexMask &curves_to_copy,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
CurvesGeometry dst_curves(0, curves_to_copy.size());
|
||||
MutableSpan<int> new_curve_offsets = dst_curves.offsets_for_write();
|
||||
offset_indices::gather_group_sizes(
|
||||
curves.points_by_curve(), curves_to_copy, new_curve_offsets.drop_back(1));
|
||||
offset_indices::accumulate_counts_to_offsets(new_curve_offsets);
|
||||
dst_curves.resize(new_curve_offsets.last(), dst_curves.curves_num());
|
||||
|
||||
const OffsetIndices src_points_by_curve = curves.points_by_curve();
|
||||
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
|
||||
const OffsetIndices dst_points_by_curve = offset_indices::gather_selected_offsets(
|
||||
points_by_curve, curves_to_copy, dst_curves.offsets_for_write());
|
||||
dst_curves.resize(dst_points_by_curve.total_size(), dst_curves.curves_num());
|
||||
|
||||
const AttributeAccessor src_attributes = curves.attributes();
|
||||
MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
|
||||
|
||||
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (meta_data.domain != ATTR_DOMAIN_POINT) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
const GVArraySpan src = *src_attributes.lookup(id);
|
||||
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
gather_group_to_group(src_points_by_curve, dst_points_by_curve, curves_to_copy, src, dst.span);
|
||||
dst.finish();
|
||||
return true;
|
||||
});
|
||||
gather_attributes_group_to_group(src_attributes,
|
||||
ATTR_DOMAIN_POINT,
|
||||
propagation_info,
|
||||
{},
|
||||
points_by_curve,
|
||||
dst_points_by_curve,
|
||||
curves_to_copy,
|
||||
dst_attributes);
|
||||
|
||||
gather_attributes(
|
||||
src_attributes, ATTR_DOMAIN_CURVE, propagation_info, {}, curves_to_copy, dst_attributes);
|
||||
|
|
|
@ -1650,8 +1650,8 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface
|
|||
return;
|
||||
}
|
||||
|
||||
/* for vertex surface loop through tfaces and find uv color
|
||||
* that provides highest alpha */
|
||||
/* For vertex surface loop through `looptris` and find UV color
|
||||
* that provides highest alpha. */
|
||||
if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
|
||||
struct ImagePool *pool = BKE_image_pool_new();
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
|
|||
|
||||
int stroke_i = 0;
|
||||
LISTBASE_FOREACH_INDEX (bGPDstroke *, gps, &gpf.strokes, stroke_i) {
|
||||
/* TODO: check if gps->editcurve is not nullptr and parse bezier curve instead. */
|
||||
/* TODO: check if `gps->editcurve` is not nullptr and parse bezier curve instead. */
|
||||
|
||||
/* Write curve attributes. */
|
||||
stroke_cyclic.span[stroke_i] = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
|
|
|
@ -2272,7 +2272,7 @@ static bool lib_override_library_resync(Main *bmain,
|
|||
RNA_OVERRIDE_APPLY_FLAG_NOP);
|
||||
|
||||
/* Clear the old shape key pointer again, otherwise it won't make ID management code happy
|
||||
* when freeing (at least from user count side of things). */
|
||||
* when freeing (at least from user count side of things). */
|
||||
if (key_override_old_p != nullptr) {
|
||||
*key_override_old_p = nullptr;
|
||||
}
|
||||
|
|
|
@ -77,8 +77,6 @@
|
|||
|
||||
static CLG_LogRef LOG = {"bke.material"};
|
||||
|
||||
static void material_clear_data(ID *id);
|
||||
|
||||
static void material_init_data(ID *id)
|
||||
{
|
||||
Material *material = (Material *)id;
|
||||
|
@ -136,26 +134,6 @@ static void material_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const
|
|||
/* TODO: Duplicate Engine Settings and set runtime to nullptr. */
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure pointers to allocated memory is cleared
|
||||
* (the kind of data that would have to be copied).
|
||||
*
|
||||
* \note Keep in sync with #material_free_data.
|
||||
* \note Doesn't handle animation data (`ma->adt`).
|
||||
*/
|
||||
static void material_clear_data(ID *id)
|
||||
{
|
||||
Material *material = (Material *)id;
|
||||
|
||||
BLI_listbase_clear(&material->gpumaterial);
|
||||
material->texpaintslot = nullptr;
|
||||
material->gp_style = nullptr;
|
||||
material->nodetree = nullptr;
|
||||
material->preview = nullptr;
|
||||
|
||||
id->icon_id = 0;
|
||||
}
|
||||
|
||||
static void material_free_data(ID *id)
|
||||
{
|
||||
Material *material = (Material *)id;
|
||||
|
@ -1934,170 +1912,6 @@ void ramp_blend(int type, float r_col[3], const float fac, const float col[3])
|
|||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Material Copy/Paste
|
||||
*
|
||||
* As materials may reference other data, the clipboard only stores a subset of all possible data.
|
||||
* The material and it's node-tree are stored and nothing else.
|
||||
* Notably the following variables are *not* part of the clipboard.
|
||||
*
|
||||
* - The #ID (name, fake-user, custom-properties .. etc).
|
||||
* - Animation data (#Material::adt, #bNodeTree::adt).
|
||||
* - Texture paint slots (#Material::texpaintslot)
|
||||
* as this is cache and references other ID's.
|
||||
* - Grease pencil style (#Material::gp_style)
|
||||
* could be supported but ID's and pointers would need to be handled carefully.
|
||||
*
|
||||
* When pasting, some data in the destination material is left as-is:
|
||||
* - The #ID.
|
||||
* - Animation data, with the exception that pasting a material without a node-tree
|
||||
* will clear the existing materials node-tree & its animation.
|
||||
* Note that applying existing animation to the pasted material might not make sense
|
||||
* and may reference data-paths that don't resolve (depending on the kind of material).
|
||||
* The user might need to clear the animation in this case.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static Material matcopybuf;
|
||||
static short matcopied = 0;
|
||||
|
||||
void BKE_material_copybuf_clear(void)
|
||||
{
|
||||
if (matcopied) {
|
||||
BKE_material_copybuf_free();
|
||||
}
|
||||
matcopybuf = blender::dna::shallow_zero_initialize();
|
||||
matcopied = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some members should *never* be set, ensure this is always the case.
|
||||
* Call at the beginning & end of functions that manipulate the clipboard.
|
||||
*/
|
||||
static void material_copybuf_assert_is_valid()
|
||||
{
|
||||
BLI_assert(!matcopybuf.id.icon_id);
|
||||
BLI_assert(!matcopybuf.id.py_instance);
|
||||
BLI_assert(!matcopybuf.adt);
|
||||
BLI_assert(!matcopybuf.preview);
|
||||
if (matcopybuf.nodetree) {
|
||||
BLI_assert(!matcopybuf.nodetree->id.py_instance);
|
||||
BLI_assert(!matcopybuf.nodetree->adt);
|
||||
BLI_assert(!matcopybuf.nodetree->owner_id);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_material_copybuf_free(void)
|
||||
{
|
||||
material_copybuf_assert_is_valid();
|
||||
|
||||
material_free_data(&matcopybuf.id);
|
||||
matcopied = 0;
|
||||
}
|
||||
|
||||
void BKE_material_copybuf_copy(Main *bmain, Material *ma)
|
||||
{
|
||||
material_copybuf_assert_is_valid();
|
||||
|
||||
if (matcopied) {
|
||||
BKE_material_copybuf_free();
|
||||
}
|
||||
|
||||
matcopybuf = blender::dna::shallow_copy(*ma);
|
||||
|
||||
/* Not essential, but we never want to use any ID values from the source,
|
||||
* this ensures that never happens. */
|
||||
memset(&matcopybuf.id, 0, sizeof(ID));
|
||||
|
||||
/* Ensure dangling pointers are never copied back into a material. */
|
||||
material_clear_data(&matcopybuf.id);
|
||||
|
||||
/* Unhandled by materials generic data functions. */
|
||||
matcopybuf.adt = nullptr;
|
||||
|
||||
if (ma->nodetree != nullptr) {
|
||||
/* Never copy animation data. */
|
||||
struct {
|
||||
AnimData *adt;
|
||||
} backup;
|
||||
backup.adt = ma->nodetree->adt;
|
||||
ma->nodetree->adt = nullptr;
|
||||
|
||||
matcopybuf.nodetree = blender::bke::ntreeCopyTree_ex(ma->nodetree, bmain, false);
|
||||
matcopybuf.nodetree->owner_id = nullptr;
|
||||
|
||||
ma->nodetree->adt = backup.adt;
|
||||
}
|
||||
|
||||
/* TODO: Duplicate Engine Settings and set runtime to nullptr. */
|
||||
matcopied = 1;
|
||||
|
||||
material_copybuf_assert_is_valid();
|
||||
}
|
||||
|
||||
bool BKE_material_copybuf_paste(Main *bmain, Material *ma)
|
||||
{
|
||||
material_copybuf_assert_is_valid();
|
||||
|
||||
if (matcopied == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* `matcopybuf` never has animation data, no need to check. */
|
||||
const bool has_animdata = (ma->adt != nullptr || (ma->nodetree && ma->nodetree->adt));
|
||||
const bool has_node_tree = (ma->nodetree || matcopybuf.nodetree);
|
||||
|
||||
AnimData *backup_nodetree_adt = nullptr;
|
||||
if (ma->nodetree && matcopybuf.nodetree) {
|
||||
/* Keep data to apply back to the new node-tree. */
|
||||
std::swap(backup_nodetree_adt, ma->nodetree->adt);
|
||||
}
|
||||
|
||||
/* Handles freeing nodes and and other run-time data (previews) for e.g. */
|
||||
material_free_data(&ma->id);
|
||||
|
||||
/* Copy from `matcopybuf` preserving some members.
|
||||
* NOTE: animation data isn't stored in the clipboard, any existing animation will be left as-is.
|
||||
* Any undesired animation will have to be manually cleared by the user. */
|
||||
{
|
||||
struct {
|
||||
ID id;
|
||||
AnimData *adt;
|
||||
} backup;
|
||||
backup.id = ma->id;
|
||||
backup.adt = ma->adt;
|
||||
|
||||
*ma = blender::dna::shallow_copy(matcopybuf);
|
||||
|
||||
ma->id = backup.id;
|
||||
ma->adt = backup.adt;
|
||||
}
|
||||
|
||||
if (matcopybuf.nodetree != nullptr) {
|
||||
BLI_assert(matcopybuf.nodetree->adt == nullptr);
|
||||
ma->nodetree = blender::bke::ntreeCopyTree_ex(matcopybuf.nodetree, bmain, false);
|
||||
ma->nodetree->owner_id = &ma->id;
|
||||
|
||||
/* Restore animation pointer (if set). */
|
||||
BLI_assert(ma->nodetree->adt == nullptr);
|
||||
ma->nodetree->adt = backup_nodetree_adt;
|
||||
}
|
||||
|
||||
if (has_node_tree || has_animdata) {
|
||||
/* Important to run this when the embedded tree if freed,
|
||||
* otherwise the depsgraph holds a reference to the (now freed) `ma->nodetree`.
|
||||
* Also run this when a new node-tree is set to ensure it's accounted for.
|
||||
* This also applies to animation data which is likely to be stored in the depsgraph. */
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
material_copybuf_assert_is_valid();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void BKE_material_eval(struct Depsgraph *depsgraph, Material *material)
|
||||
{
|
||||
DEG_debug_print_eval(depsgraph, __func__, material->id.name, material);
|
||||
|
|
|
@ -1178,7 +1178,7 @@ static int mesh_tessface_calc(Mesh &mesh,
|
|||
|
||||
/* NOTE: quad detection issue - fourth vertex-index vs fourth loop-index:
|
||||
* Polygons take care of their loops ordering, hence not of their vertices ordering.
|
||||
* Currently, our tfaces' fourth vertex index might be 0 even for a quad.
|
||||
* Currently, the #TFace fourth vertex index might be 0 even for a quad.
|
||||
* However, we know our fourth loop index is never 0 for quads
|
||||
* (because they are sorted for polygons, and our quads are still mere copies of their polygons).
|
||||
* So we pass nullptr as #MFace pointer, and #mesh_loops_to_tessdata
|
||||
|
|
|
@ -1776,7 +1776,7 @@ void nodeModifySocketType(bNodeTree *ntree,
|
|||
if (sock->type != socktype->type) {
|
||||
/* Only reallocate the default value if the type changed so that UI data like min and max
|
||||
* isn't removed. This assumes that the default value is stored in the same format for all
|
||||
* socket types with the same #eNodeSocketDatatype. */
|
||||
* socket types with the same #eNodeSocketDatatype. */
|
||||
socket_id_user_decrement(sock);
|
||||
MEM_freeN(sock->default_value);
|
||||
sock->default_value = nullptr;
|
||||
|
|
|
@ -228,7 +228,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
|
|||
|
||||
/* Embedded ID pointers are not remapped (besides exceptions), ensure it still matches
|
||||
* actual data. Note that `snode->id` was already processed (and therefore potentially
|
||||
* remapped) above.*/
|
||||
* remapped) above. */
|
||||
if (!is_readonly) {
|
||||
snode->nodetree = (snode->id == NULL) ? NULL : ntreeFromID(snode->id);
|
||||
if (path != NULL) {
|
||||
|
|
|
@ -149,7 +149,7 @@ class ImplicitSharingInfo : NonCopyable, NonMovable {
|
|||
* data can be freed though. */
|
||||
const_cast<ImplicitSharingInfo *>(this)->delete_data_only();
|
||||
/* Also remove the "fake" weak user that indicated that there was at least one strong
|
||||
* user.*/
|
||||
* user. */
|
||||
this->remove_weak_user_and_delete_if_last();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ template<typename T> class OffsetIndices {
|
|||
/** Return the total number of elements in the referenced arrays. */
|
||||
T total_size() const
|
||||
{
|
||||
return offsets_.size() == 1 ? 0 : offsets_.last();
|
||||
return offsets_.size() > 1 ? offsets_.last() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,6 +146,10 @@ void copy_group_sizes(OffsetIndices<int> offsets, const IndexMask &mask, Mutable
|
|||
/** Gather the number of indices in each indexed group to sizes. */
|
||||
void gather_group_sizes(OffsetIndices<int> offsets, const IndexMask &mask, MutableSpan<int> sizes);
|
||||
|
||||
/** Build new offsets that contains only the groups chosen by \a selection. */
|
||||
OffsetIndices<int> gather_selected_offsets(OffsetIndices<int> src_offsets,
|
||||
const IndexMask &selection,
|
||||
MutableSpan<int> dst_offsets);
|
||||
/**
|
||||
* Create a map from indexed elements to the source indices, in other words from the larger array
|
||||
* to the smaller array.
|
||||
|
|
|
@ -61,7 +61,7 @@ template<typename T> class SharedCache {
|
|||
}
|
||||
|
||||
/** Retrieve the cached data. */
|
||||
const T &data()
|
||||
const T &data() const
|
||||
{
|
||||
BLI_assert(cache_->mutex.is_cached());
|
||||
return cache_->data;
|
||||
|
|
|
@ -436,6 +436,15 @@ void BLI_str_rstrip(char *str) ATTR_NONNULL(1);
|
|||
*/
|
||||
int BLI_str_rstrip_float_zero(char *str, char pad) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Strip trailing digits.
|
||||
* ABC123 -> ABC
|
||||
*
|
||||
* \param str:
|
||||
* \return The number of digits stripped.
|
||||
*/
|
||||
int BLI_str_rstrip_digits(char *str) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Return index of a string in a string array.
|
||||
*
|
||||
|
|
|
@ -17,7 +17,13 @@
|
|||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# include <windows.h>
|
||||
# undef NOMINMAX
|
||||
#else
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#undef rad
|
||||
#undef rad1
|
||||
|
|
|
@ -343,7 +343,7 @@ bool BLI_dir_create_recursive(const char *dirname)
|
|||
if (mode != 0) {
|
||||
/* The file exists, either it's a directory (ok), or not,
|
||||
* in which case this function can't do anything useful
|
||||
* (the caller could remove it and re-run this function). */
|
||||
* (the caller could remove it and re-run this function). */
|
||||
return S_ISDIR(mode) ? true : false;
|
||||
}
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ static int64_t get_size_before_gap(const Span<int16_t> indices)
|
|||
BLI_assert(indices.size() >= 2);
|
||||
if (indices[1] > indices[0] + 1) {
|
||||
/* For sparse indices, often the next gap is just after the next index.
|
||||
* In this case we can skip the logarithmic check below.*/
|
||||
* In this case we can skip the logarithmic check below. */
|
||||
return 1;
|
||||
}
|
||||
return unique_sorted_indices::find_size_of_next_range(indices);
|
||||
|
|
|
@ -38,6 +38,19 @@ void gather_group_sizes(const OffsetIndices<int> offsets,
|
|||
});
|
||||
}
|
||||
|
||||
OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,
|
||||
const IndexMask &selection,
|
||||
MutableSpan<int> dst_offsets)
|
||||
{
|
||||
if (selection.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
BLI_assert(selection.size() == (dst_offsets.size() - 1));
|
||||
gather_group_sizes(src_offsets, selection, dst_offsets);
|
||||
accumulate_counts_to_offsets(dst_offsets);
|
||||
return OffsetIndices<int>(dst_offsets);
|
||||
}
|
||||
|
||||
void build_reverse_map(OffsetIndices<int> offsets, MutableSpan<int> r_map)
|
||||
{
|
||||
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
|
||||
|
|
|
@ -1058,6 +1058,17 @@ int BLI_str_rstrip_float_zero(char *str, const char pad)
|
|||
return totstrip;
|
||||
}
|
||||
|
||||
int BLI_str_rstrip_digits(char *str)
|
||||
{
|
||||
int totstrip = 0;
|
||||
int str_len = strlen(str);
|
||||
while (str_len > 0 && isdigit(str[--str_len])) {
|
||||
str[str_len] = '\0';
|
||||
totstrip++;
|
||||
}
|
||||
return totstrip;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -125,7 +125,7 @@ TEST(string, StrCopyUTF8_TerminateEncodingEarly)
|
|||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name String Concatinate
|
||||
/** \name String Concatenate
|
||||
* \{ */
|
||||
|
||||
TEST(string, StrCat)
|
||||
|
|
|
@ -86,7 +86,7 @@ class AntiAliasing {
|
|||
pass.framebuffer_set(&output_fb_);
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
|
||||
pass.shader_set(shaders_.static_shader_get(ANTIALIASING_RESOLVE));
|
||||
/** \note use color_tx as dummy if AA is diabled. */
|
||||
/** \note use color_tx as dummy if AA is disabled. */
|
||||
pass.bind_texture("blendTex", anti_aliasing_enabled_ ? &blend_weight_tx_ : &color_tx);
|
||||
pass.bind_texture("colorTex", &color_tx);
|
||||
pass.bind_texture("revealTex", &reveal_tx);
|
||||
|
|
|
@ -103,7 +103,7 @@ class Instance {
|
|||
|
||||
objects.init(v3d, scene);
|
||||
lights.init(v3d);
|
||||
/* TODO(fclem): Vfx. */
|
||||
/* TODO(@fclem): VFX. */
|
||||
// vfx.init(use_vfx_, camera_, rv3d);
|
||||
anti_aliasing.init(v3d, scene);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
namespace blender::draw::greasepencil {
|
||||
|
||||
enum eShaderType {
|
||||
/* SMAA antialiasing */
|
||||
/* SMAA anti-aliasing. */
|
||||
ANTIALIASING_EDGE_DETECT = 0,
|
||||
ANTIALIASING_BLEND_WEIGHT,
|
||||
ANTIALIASING_RESOLVE,
|
||||
|
|
|
@ -133,7 +133,7 @@ BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
|
|||
#endif
|
||||
|
||||
struct gpObject {
|
||||
/** Wether or not to apply lighting to the GPencil object. */
|
||||
/** Weather or not to apply lighting to the GPencil object. */
|
||||
bool1 is_shadeless;
|
||||
/** Switch between 2d and 3D stroke order. */
|
||||
bool1 stroke_order3d;
|
||||
|
|
|
@ -18,8 +18,10 @@
|
|||
|
||||
namespace blender::draw::overlay {
|
||||
|
||||
/* Selection engine reuse most of the Overlay engine by creating selection IDs for each
|
||||
* selectable component and using a special shaders for drawing.*/
|
||||
/**
|
||||
* Selection engine reuse most of the Overlay engine by creating selection IDs for each
|
||||
* selectable component and using a special shaders for drawing.
|
||||
*/
|
||||
class Instance {
|
||||
const SelectionType selection_type_;
|
||||
|
||||
|
|
|
@ -160,12 +160,12 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har
|
|||
/* Aspect uses 9 bits */
|
||||
float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
|
||||
packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
|
||||
/* Store if inversed in the 9th bit. */
|
||||
/* Store if inverted in the 9th bit. */
|
||||
if (asp > 1.0f) {
|
||||
packed |= 1 << 8;
|
||||
}
|
||||
/* Rotation uses 9 bits */
|
||||
/* Rotation are in [-90°..90°] range, so we can encode the sign of the angle + the cosine
|
||||
/* Rotation are in [-90..90] degree range, so we can encode the sign of the angle + the cosine
|
||||
* because the cosine will always be positive. */
|
||||
packed |= int32_t(unit_float_to_uchar_clamp(cosf(rot))) << 9;
|
||||
/* Store sine sign in 9th bit. */
|
||||
|
@ -197,7 +197,7 @@ static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
|
|||
cfra, [&](GreasePencilDrawing &drawing) { drawings.append(&drawing); });
|
||||
|
||||
/* First, count how many vertices and triangles are needed for the whole object. Also record the
|
||||
* offsets into the curves for the verticies and triangles. */
|
||||
* offsets into the curves for the vertices and triangles. */
|
||||
int total_points_num = 0;
|
||||
int total_triangles_num = 0;
|
||||
int v_offset = 0;
|
||||
|
@ -240,7 +240,7 @@ static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
|
|||
}
|
||||
|
||||
/* One vertex is stored before and after as padding. Cyclic strokes have one extra
|
||||
* vertex.*/
|
||||
* vertex. */
|
||||
total_points_num += curves.points_num() + num_cyclic + curves.curves_num() * 2;
|
||||
total_triangles_num += (curves.points_num() + num_cyclic) * 2;
|
||||
total_triangles_num += drawing.triangles().size();
|
||||
|
|
|
@ -3354,7 +3354,7 @@ static int click_select_channel_object(bContext *C,
|
|||
* to avoid getting stuck there, see: #48747. */
|
||||
ED_object_base_activate_with_mode_exit_if_needed(C, base); /* adds notifier */
|
||||
|
||||
/* Similar to outliner, do not change active element when selecting elements in range.*/
|
||||
/* Similar to outliner, do not change active element when selecting elements in range. */
|
||||
if ((adt) && (adt->flag & ADT_UI_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
|
||||
adt->flag |= ADT_UI_ACTIVE;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,17 @@
|
|||
|
||||
#include "armature_intern.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Local Utilities
|
||||
* \{ */
|
||||
|
||||
static void pose_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
|
||||
{
|
||||
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer_pose.blend");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Apply Pose as Rest Pose
|
||||
* \{ */
|
||||
|
@ -789,7 +800,8 @@ static int pose_copy_exec(bContext *C, wmOperator *op)
|
|||
* existing on its own.
|
||||
*/
|
||||
BKE_copybuffer_copy_tag_ID(&ob_copy.id);
|
||||
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer_pose.blend");
|
||||
|
||||
pose_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
BKE_copybuffer_copy_end(temp_bmain, filepath, op->reports);
|
||||
/* We clear the lists so no datablocks gets freed,
|
||||
* This is required because objects in temp bmain shares same pointers
|
||||
|
@ -842,27 +854,27 @@ static int pose_paste_exec(bContext *C, wmOperator *op)
|
|||
|
||||
/* Read copy buffer .blend file. */
|
||||
char filepath[FILE_MAX];
|
||||
Main *tmp_bmain = BKE_main_new();
|
||||
STRNCPY(tmp_bmain->filepath, BKE_main_blendfile_path_from_global());
|
||||
Main *temp_bmain = BKE_main_new();
|
||||
STRNCPY(temp_bmain->filepath, BKE_main_blendfile_path_from_global());
|
||||
|
||||
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer_pose.blend");
|
||||
if (!BKE_copybuffer_read(tmp_bmain, filepath, op->reports, FILTER_ID_OB)) {
|
||||
pose_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
if (!BKE_copybuffer_read(temp_bmain, filepath, op->reports, FILTER_ID_OB)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is empty");
|
||||
BKE_main_free(tmp_bmain);
|
||||
BKE_main_free(temp_bmain);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
/* Make sure data from this file is usable for pose paste. */
|
||||
if (BLI_listbase_count_at_most(&tmp_bmain->objects, 2) != 1) {
|
||||
if (BLI_listbase_count_at_most(&temp_bmain->objects, 2) != 1) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is not from pose mode");
|
||||
BKE_main_free(tmp_bmain);
|
||||
BKE_main_free(temp_bmain);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
Object *object_from = tmp_bmain->objects.first;
|
||||
Object *object_from = temp_bmain->objects.first;
|
||||
bPose *pose_from = object_from->pose;
|
||||
if (pose_from == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Internal clipboard has no pose");
|
||||
BKE_main_free(tmp_bmain);
|
||||
BKE_main_free(temp_bmain);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
|
@ -889,7 +901,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
}
|
||||
}
|
||||
BKE_main_free(tmp_bmain);
|
||||
BKE_main_free(temp_bmain);
|
||||
|
||||
/* Update event for pose and deformation children. */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
|
|
|
@ -1688,7 +1688,7 @@ static int gpencil_weight_sample_invoke(bContext *C, wmOperator *UNUSED(op), con
|
|||
}
|
||||
}
|
||||
|
||||
/* Set brush weight, based on points found.*/
|
||||
/* Set brush weight, based on points found. */
|
||||
if (closest_count > 0) {
|
||||
if (closest_count == 1) {
|
||||
brush->weight = closest_weight[0];
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,8 +22,9 @@ extern "C" {
|
|||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
void create_blank(Main &bmain, Object &object, int frame_numer);
|
||||
void create_stroke(Main &bmain, Object &object, float4x4 matrix, int frame_numer);
|
||||
void create_blank(Main &bmain, Object &object, int frame_number);
|
||||
void create_stroke(Main &bmain, Object &object, float4x4 matrix, int frame_number);
|
||||
void create_suzanne(Main &bmain, Object &object, float4x4 matrix, const int frame_number);
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
#endif
|
||||
|
|
|
@ -427,6 +427,8 @@ void paintface_select_linked(struct bContext *C,
|
|||
struct Object *ob,
|
||||
const int mval[2],
|
||||
bool select);
|
||||
|
||||
void paintface_select_loop(struct bContext *C, struct Object *ob, const int mval[2], bool select);
|
||||
/**
|
||||
* Grow the selection of faces.
|
||||
* \param face_step: If true will also select faces that only touch on the corner.
|
||||
|
|
|
@ -52,8 +52,8 @@ typedef enum {
|
|||
TFM_TIME_SLIDE,
|
||||
TFM_TIME_SCALE,
|
||||
TFM_TIME_EXTEND,
|
||||
TFM_TIME_DUPLICATE,
|
||||
TFM_BAKE_TIME,
|
||||
/* TFM_TIME_DUPLICATE (deprecated). */
|
||||
TFM_BAKE_TIME = 26,
|
||||
TFM_DEPRECATED, /* was BEVEL */
|
||||
TFM_BWEIGHT,
|
||||
TFM_ALIGN,
|
||||
|
|
|
@ -1332,7 +1332,7 @@ void ED_view3d_shade_update(struct Main *bmain, struct View3D *v3d, struct ScrAr
|
|||
#define OVERLAY_RETOPOLOGY_ENABLED(overlay) \
|
||||
(((overlay).edit_flag & V3D_OVERLAY_EDIT_RETOPOLOGY) != 0)
|
||||
#ifdef __APPLE__
|
||||
/* Apple silicon tile depth test requires a higher value to reduce drawing artifacts.*/
|
||||
/* Apple silicon tile depth test requires a higher value to reduce drawing artifacts. */
|
||||
# define OVERLAY_RETOPOLOGY_MIN_OFFSET_ENABLED 0.0015f
|
||||
# define OVERLAY_RETOPOLOGY_MIN_OFFSET_DISABLED 0.0015f
|
||||
#else
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
#include "BLI_bitmap.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
@ -350,6 +352,177 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b
|
|||
paintface_flush_flags(C, ob, true, false);
|
||||
}
|
||||
|
||||
static int find_closest_edge_in_poly(ARegion *region,
|
||||
blender::Span<blender::int2> edges,
|
||||
blender::Span<int> poly_edges,
|
||||
blender::Span<blender::float3> vert_positions,
|
||||
const int mval[2])
|
||||
{
|
||||
using namespace blender;
|
||||
int closest_edge_index;
|
||||
|
||||
const float2 mval_f = {float(mval[0]), float(mval[1])};
|
||||
float min_distance = FLT_MAX;
|
||||
for (const int i : poly_edges) {
|
||||
float2 screen_coordinate;
|
||||
const int2 edge = edges[i];
|
||||
const float3 edge_vert_average = math::midpoint(vert_positions[edge[0]],
|
||||
vert_positions[edge[1]]);
|
||||
eV3DProjStatus status = ED_view3d_project_float_object(
|
||||
region, edge_vert_average, screen_coordinate, V3D_PROJ_TEST_CLIP_DEFAULT);
|
||||
if (status != V3D_PROJ_RET_OK) {
|
||||
continue;
|
||||
}
|
||||
const float distance = math::distance_squared(mval_f, screen_coordinate);
|
||||
if (distance < min_distance) {
|
||||
min_distance = distance;
|
||||
closest_edge_index = i;
|
||||
}
|
||||
}
|
||||
return closest_edge_index;
|
||||
}
|
||||
|
||||
static int get_opposing_edge_index(const blender::IndexRange poly,
|
||||
const blender::Span<int> corner_edges,
|
||||
const int current_edge_index)
|
||||
{
|
||||
const int index_in_poly = corner_edges.slice(poly).first_index(current_edge_index);
|
||||
/* Assumes that edge index of opposing face edge is always off by 2 on quads. */
|
||||
if (index_in_poly >= 2) {
|
||||
return corner_edges[poly[index_in_poly - 2]];
|
||||
}
|
||||
/* Cannot be out of bounds because of the preceding if statement: if i < 2 then i+2 < 4. */
|
||||
return corner_edges[poly[index_in_poly + 2]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow quads around the mesh by finding opposing edges.
|
||||
* \return True if the search has looped back on itself, finding the same index twice.
|
||||
*/
|
||||
static bool follow_face_loop(const int poly_start_index,
|
||||
const int edge_start_index,
|
||||
const blender::OffsetIndices<int> polys,
|
||||
const blender::VArray<bool> &hide_poly,
|
||||
const blender::Span<int> corner_edges,
|
||||
const blender::GroupedSpan<int> edge_to_poly_map,
|
||||
blender::VectorSet<int> &r_loop_polys)
|
||||
{
|
||||
using namespace blender;
|
||||
int current_poly_index = poly_start_index;
|
||||
int current_edge_index = edge_start_index;
|
||||
|
||||
while (current_edge_index > 0) {
|
||||
int next_poly_index = -1;
|
||||
|
||||
for (const int poly_index : edge_to_poly_map[current_edge_index]) {
|
||||
if (poly_index != current_poly_index) {
|
||||
next_poly_index = poly_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Edge might only have 1 poly connected. */
|
||||
if (next_poly_index == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Only works on quads. */
|
||||
if (polys[next_poly_index].size() != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Happens if we looped around the mesh. */
|
||||
if (r_loop_polys.contains(next_poly_index)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Hidden polygons stop selection. */
|
||||
if (hide_poly[next_poly_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r_loop_polys.add(next_poly_index);
|
||||
|
||||
const IndexRange next_poly = polys[next_poly_index];
|
||||
current_edge_index = get_opposing_edge_index(next_poly, corner_edges, current_edge_index);
|
||||
current_poly_index = next_poly_index;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void paintface_select_loop(bContext *C, Object *ob, const int mval[2], const bool select)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
ViewContext vc;
|
||||
ED_view3d_viewcontext_init(C, &vc, depsgraph);
|
||||
ED_view3d_select_id_validate(&vc);
|
||||
|
||||
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
||||
if (!ob_eval) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint poly_pick_index = uint(-1);
|
||||
if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &poly_pick_index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
||||
ED_view3d_init_mats_rv3d(ob_eval, rv3d);
|
||||
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
const Span<int> corner_edges = mesh->corner_edges();
|
||||
const Span<float3> verts = mesh->vert_positions();
|
||||
const OffsetIndices polys = mesh->polys();
|
||||
const Span<int2> edges = mesh->edges();
|
||||
|
||||
const IndexRange poly = polys[poly_pick_index];
|
||||
const int closest_edge_index = find_closest_edge_in_poly(
|
||||
region, edges, corner_edges.slice(poly), verts, mval);
|
||||
|
||||
Array<int> edge_to_poly_offsets;
|
||||
Array<int> edge_to_poly_indices;
|
||||
const GroupedSpan<int> edge_to_poly_map = bke::mesh::build_edge_to_poly_map(
|
||||
polys, corner_edges, mesh->totedge, edge_to_poly_offsets, edge_to_poly_indices);
|
||||
|
||||
VectorSet<int> polys_to_select;
|
||||
|
||||
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
|
||||
".hide_poly", ATTR_DOMAIN_FACE, false);
|
||||
|
||||
const Span<int> polys_to_closest_edge = edge_to_poly_map[closest_edge_index];
|
||||
const bool traced_full_loop = follow_face_loop(polys_to_closest_edge[0],
|
||||
closest_edge_index,
|
||||
polys,
|
||||
hide_poly,
|
||||
corner_edges,
|
||||
edge_to_poly_map,
|
||||
polys_to_select);
|
||||
|
||||
if (!traced_full_loop) {
|
||||
/* Trace the other way. */
|
||||
follow_face_loop(polys_to_closest_edge[1],
|
||||
closest_edge_index,
|
||||
polys,
|
||||
hide_poly,
|
||||
corner_edges,
|
||||
edge_to_poly_map,
|
||||
polys_to_select);
|
||||
}
|
||||
|
||||
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".select_poly", ATTR_DOMAIN_FACE);
|
||||
|
||||
select_poly.span.fill_indices(polys_to_select.as_span(), select);
|
||||
|
||||
select_poly.finish();
|
||||
paintface_flush_flags(C, ob, true, false);
|
||||
}
|
||||
|
||||
static bool poly_has_selected_neighbor(blender::Span<int> poly_edges,
|
||||
blender::Span<blender::int2> edges,
|
||||
blender::Span<bool> select_vert,
|
||||
|
|
|
@ -1643,7 +1643,16 @@ static int object_grease_pencil_add_exec(bContext *C, wmOperator *op)
|
|||
greasepencil::create_stroke(*bmain, *object, mat, scene->r.cfra);
|
||||
break;
|
||||
}
|
||||
case GP_MONKEY:
|
||||
case GP_MONKEY: {
|
||||
const float radius = RNA_float_get(op->ptr, "radius");
|
||||
const float3 scale(radius);
|
||||
|
||||
float4x4 mat;
|
||||
ED_object_new_primitive_matrix(C, object, loc, rot, scale, mat.ptr());
|
||||
|
||||
greasepencil::create_suzanne(*bmain, *object, mat, scene->r.cfra);
|
||||
break;
|
||||
}
|
||||
case GP_LRT_OBJECT:
|
||||
case GP_LRT_SCENE:
|
||||
case GP_LRT_COLLECTION: {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_light_types.h"
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
@ -24,12 +25,15 @@
|
|||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_appdir.h"
|
||||
#include "BKE_blender_copybuffer.h"
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curve.h"
|
||||
|
@ -38,6 +42,8 @@
|
|||
#include "BKE_image.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_query.h"
|
||||
#include "BKE_lib_remap.h"
|
||||
#include "BKE_lightprobe.h"
|
||||
#include "BKE_linestyle.h"
|
||||
#include "BKE_main.h"
|
||||
|
@ -95,6 +101,11 @@ static bool object_materials_supported_poll_ex(bContext *C, const Object *ob);
|
|||
/** \name Local Utilities
|
||||
* \{ */
|
||||
|
||||
static void material_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
|
||||
{
|
||||
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer_material.blend");
|
||||
}
|
||||
|
||||
static bool object_array_for_shading_edit_mode_enabled_filter(const Object *ob, void *user_data)
|
||||
{
|
||||
bContext *C = static_cast<bContext *>(user_data);
|
||||
|
@ -2731,8 +2742,7 @@ void TEXTURE_OT_slot_move(wmOperatorType *ot)
|
|||
/** \name Material Copy Operator
|
||||
* \{ */
|
||||
|
||||
/* material copy/paste */
|
||||
static int copy_material_exec(bContext *C, wmOperator * /*op*/)
|
||||
static int copy_material_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Material *ma = static_cast<Material *>(
|
||||
CTX_data_pointer_get_type(C, "material", &RNA_Material).data);
|
||||
|
@ -2741,7 +2751,19 @@ static int copy_material_exec(bContext *C, wmOperator * /*op*/)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BKE_material_copybuf_copy(CTX_data_main(C), ma);
|
||||
char filepath[FILE_MAX];
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
/* Mark is the material to use (others may be expanded). */
|
||||
BKE_copybuffer_copy_begin(bmain);
|
||||
|
||||
BKE_copybuffer_copy_tag_ID(&ma->id);
|
||||
|
||||
material_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
BKE_copybuffer_copy_end(bmain, filepath, op->reports);
|
||||
|
||||
/* We are all done! */
|
||||
BKE_report(op->reports, RPT_INFO, "Copied material to internal clipboard");
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
@ -2767,8 +2789,47 @@ void MATERIAL_OT_copy(wmOperatorType *ot)
|
|||
/** \name Material Paste Operator
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Clear ID's as freeing the data-block doesn't handle reference counting.
|
||||
*/
|
||||
static int paste_material_nodetree_ids_decref(LibraryIDLinkCallbackData *cb_data)
|
||||
{
|
||||
if (cb_data->cb_flag & IDWALK_CB_USER) {
|
||||
id_us_min(*cb_data->id_pointer);
|
||||
}
|
||||
*cb_data->id_pointer = nullptr;
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-map ID's from the clipboard to ID's in `bmain`, by name.
|
||||
*/
|
||||
static int paste_material_nodetree_ids_relink_or_clear(LibraryIDLinkCallbackData *cb_data)
|
||||
{
|
||||
Main *bmain = static_cast<Main *>(cb_data->user_data);
|
||||
ID **id_p = cb_data->id_pointer;
|
||||
if (*id_p) {
|
||||
if (cb_data->cb_flag & IDWALK_CB_USER) {
|
||||
id_us_min(*id_p);
|
||||
}
|
||||
ListBase *lb = which_libbase(bmain, GS((*id_p)->name));
|
||||
ID *id_local = static_cast<ID *>(
|
||||
BLI_findstring(lb, (*id_p)->name + 2, offsetof(ID, name) + 2));
|
||||
*id_p = id_local;
|
||||
if (cb_data->cb_flag & IDWALK_CB_USER) {
|
||||
id_us_plus(id_local);
|
||||
}
|
||||
else if (cb_data->cb_flag & IDWALK_CB_USER_ONE) {
|
||||
id_us_ensure_real(id_local);
|
||||
}
|
||||
id_lib_extern(id_local);
|
||||
}
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
static int paste_material_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Material *ma = static_cast<Material *>(
|
||||
CTX_data_pointer_get_type(C, "material", &RNA_Material).data);
|
||||
|
||||
|
@ -2777,11 +2838,133 @@ static int paste_material_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (!BKE_material_copybuf_paste(CTX_data_main(C), ma)) {
|
||||
BKE_report(op->reports, RPT_WARNING, "No material in the internal clipboard to paste");
|
||||
/* Read copy buffer .blend file. */
|
||||
char filepath[FILE_MAX];
|
||||
Main *temp_bmain = BKE_main_new();
|
||||
|
||||
STRNCPY(temp_bmain->filepath, BKE_main_blendfile_path_from_global());
|
||||
|
||||
material_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
|
||||
/* NOTE(@ideasman42) The node tree might reference different kinds of ID types.
|
||||
* It's not clear-cut which ID types should be included, although it's unlikely
|
||||
* users would want an entire scene & it's objects to be included.
|
||||
* Filter a subset of ID types with some reasons for including them. */
|
||||
const uint64_t ntree_filter = (
|
||||
/* Material is necessary for reading the clipboard. */
|
||||
FILTER_ID_MA |
|
||||
/* Node-groups. */
|
||||
FILTER_ID_NT |
|
||||
/* Image textures. */
|
||||
FILTER_ID_IM |
|
||||
/* Internal text (scripts). */
|
||||
FILTER_ID_TXT |
|
||||
/* Texture coordinates may reference objects.
|
||||
* Note that object data is *not* included. */
|
||||
FILTER_ID_OB);
|
||||
|
||||
if (!BKE_copybuffer_read(temp_bmain, filepath, op->reports, ntree_filter)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is empty");
|
||||
BKE_main_free(temp_bmain);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* There may be multiple materials,
|
||||
* check for a property that marks this as the active material. */
|
||||
Material *ma_from = nullptr;
|
||||
LISTBASE_FOREACH (Material *, ma_iter, &temp_bmain->materials) {
|
||||
if (ma_iter->id.flag & LIB_CLIPBOARD_MARK) {
|
||||
ma_from = ma_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure data from this file is usable for material paste. */
|
||||
if (ma_from == nullptr) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is not from a material");
|
||||
BKE_main_free(temp_bmain);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Keep animation by moving local animation to the paste node-tree. */
|
||||
if (ma->nodetree && ma_from->nodetree) {
|
||||
BLI_assert(ma_from->nodetree->adt == nullptr);
|
||||
std::swap(ma->nodetree->adt, ma_from->nodetree->adt);
|
||||
}
|
||||
|
||||
/* Needed to update #SpaceNode::nodetree else a stale pointer is used. */
|
||||
if (ma->nodetree) {
|
||||
bNodeTree *nodetree = ma->nodetree;
|
||||
BKE_libblock_remap(bmain, ma->nodetree, ma_from->nodetree, ID_REMAP_FORCE_UI_POINTERS);
|
||||
|
||||
/* Free & clear data here, so user counts are handled, otherwise it's
|
||||
* freed as part of #BKE_main_free which doesn't handle user-counts. */
|
||||
/* Walk over all the embedded nodes ID's (non-recursively). */
|
||||
BKE_library_foreach_ID_link(
|
||||
bmain, &nodetree->id, paste_material_nodetree_ids_decref, nullptr, IDWALK_NOP);
|
||||
|
||||
ntreeFreeEmbeddedTree(nodetree);
|
||||
MEM_freeN(nodetree);
|
||||
ma->nodetree = nullptr;
|
||||
}
|
||||
|
||||
/* Swap data-block content, while swapping isn't always needed,
|
||||
* it means memory is properly freed in the case of allocations.. */
|
||||
#define SWAP_MEMBER(member) std::swap(ma->member, ma_from->member)
|
||||
|
||||
/* Intentionally skip:
|
||||
* - Texture painting slots.
|
||||
* - Preview render.
|
||||
* - Grease pencil styles (we could although they reference many ID's themselves).
|
||||
*/
|
||||
SWAP_MEMBER(flag);
|
||||
SWAP_MEMBER(r);
|
||||
SWAP_MEMBER(g);
|
||||
SWAP_MEMBER(b);
|
||||
SWAP_MEMBER(a);
|
||||
SWAP_MEMBER(specr);
|
||||
SWAP_MEMBER(specg);
|
||||
SWAP_MEMBER(specb);
|
||||
SWAP_MEMBER(spec);
|
||||
SWAP_MEMBER(roughness);
|
||||
SWAP_MEMBER(metallic);
|
||||
SWAP_MEMBER(use_nodes);
|
||||
SWAP_MEMBER(index);
|
||||
SWAP_MEMBER(nodetree);
|
||||
SWAP_MEMBER(line_col);
|
||||
SWAP_MEMBER(line_priority);
|
||||
SWAP_MEMBER(vcol_alpha);
|
||||
|
||||
SWAP_MEMBER(alpha_threshold);
|
||||
SWAP_MEMBER(refract_depth);
|
||||
SWAP_MEMBER(blend_method);
|
||||
SWAP_MEMBER(blend_shadow);
|
||||
SWAP_MEMBER(blend_flag);
|
||||
|
||||
SWAP_MEMBER(lineart);
|
||||
|
||||
#undef SWAP_MEMBER
|
||||
|
||||
/* The node-tree from the clipboard is now assigned to the local material,
|
||||
* however the ID's it references are still part of `temp_bmain`.
|
||||
* These data-blocks references must be cleared or replaces with references to `bmain`.
|
||||
* TODO(@ideasman42): support merging indirectly referenced data-blocks besides the material,
|
||||
* this would be useful for pasting materials with node-groups between files. */
|
||||
if (ma->nodetree) {
|
||||
ma->nodetree->owner_id = nullptr;
|
||||
/* Map remote ID's to local ones. */
|
||||
BKE_library_foreach_ID_link(
|
||||
bmain, &ma->nodetree->id, paste_material_nodetree_ids_relink_or_clear, bmain, IDWALK_NOP);
|
||||
}
|
||||
BKE_main_free(temp_bmain);
|
||||
|
||||
/* Important to run this when the embedded tree if freed,
|
||||
* otherwise the depsgraph holds a reference to the (now freed) `ma->nodetree`.
|
||||
* Also run this when a new node-tree is set to ensure it's accounted for.
|
||||
* This also applies to animation data which is likely to be stored in the depsgraph.
|
||||
* Always call instead of checking when it *might* be needed. */
|
||||
DEG_relations_tag_update(bmain);
|
||||
|
||||
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
|
||||
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
|
||||
|
||||
|
@ -2870,7 +3053,22 @@ static void paste_mtex_copybuf(ID *id)
|
|||
|
||||
**mtex = blender::dna::shallow_copy(mtexcopybuf);
|
||||
|
||||
id_us_plus((ID *)mtexcopybuf.tex);
|
||||
/* NOTE(@ideasman42): the simple memory copy has no special handling for ID data-blocks.
|
||||
* Ideally this would use `BKE_copybuffer_*` API's, however for common using
|
||||
* copy-pasting between slots, the case a users expects to copy between files
|
||||
* seems quite niche. So, do primitive ID validation. */
|
||||
|
||||
/* WARNING: This isn't a fool-proof solution as it's possible memory locations are reused,
|
||||
* or that the ID was relocated in memory since it was copied.
|
||||
* it does however guard against references to dangling pointers. */
|
||||
if ((*mtex)->tex && (BLI_findindex(&G_MAIN->textures, (*mtex)->tex) == -1)) {
|
||||
(*mtex)->tex = nullptr;
|
||||
}
|
||||
if ((*mtex)->object && (BLI_findindex(&G_MAIN->objects, (*mtex)->object) == -1)) {
|
||||
(*mtex)->object = nullptr;
|
||||
}
|
||||
id_us_plus((ID *)(*mtex)->tex);
|
||||
id_lib_extern((ID *)(*mtex)->object);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -369,6 +369,7 @@ void PAINT_OT_face_select_all(wmOperatorType *ot);
|
|||
void PAINT_OT_face_select_more(wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_less(wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_hide(wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_loop(wmOperatorType *ot);
|
||||
|
||||
void PAINT_OT_face_vert_reveal(wmOperatorType *ot);
|
||||
|
||||
|
|
|
@ -1540,6 +1540,7 @@ void ED_operatortypes_paint(void)
|
|||
WM_operatortype_append(PAINT_OT_face_select_more);
|
||||
WM_operatortype_append(PAINT_OT_face_select_less);
|
||||
WM_operatortype_append(PAINT_OT_face_select_hide);
|
||||
WM_operatortype_append(PAINT_OT_face_select_loop);
|
||||
|
||||
WM_operatortype_append(PAINT_OT_face_vert_reveal);
|
||||
|
||||
|
|
|
@ -767,6 +767,34 @@ void PAINT_OT_face_select_less(wmOperatorType *ot)
|
|||
ot->srna, "face_step", true, "Face Step", "Also deselect faces that only touch on a corner");
|
||||
}
|
||||
|
||||
static int paintface_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const bool select = RNA_boolean_get(op->ptr, "select");
|
||||
const bool extend = RNA_boolean_get(op->ptr, "extend");
|
||||
if (!extend) {
|
||||
paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, false);
|
||||
}
|
||||
view3d_operator_needs_opengl(C);
|
||||
paintface_select_loop(C, CTX_data_active_object(C), event->mval, select);
|
||||
ED_region_tag_redraw(CTX_wm_region(C));
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void PAINT_OT_face_select_loop(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select Loop";
|
||||
ot->description = "Select face loop under the cursor";
|
||||
ot->idname = "PAINT_OT_face_select_loop";
|
||||
|
||||
ot->invoke = paintface_select_loop_invoke;
|
||||
ot->poll = facemask_paint_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(ot->srna, "select", true, "Select", "If false, faces will be deselected");
|
||||
RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
|
||||
}
|
||||
|
||||
static int vert_select_all_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
|
|
@ -84,7 +84,8 @@ void ED_operatormacros_action(void)
|
|||
OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
WM_operatortype_macro_define(ot, "ACTION_OT_duplicate");
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_transform");
|
||||
RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_DUPLICATE);
|
||||
RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_TRANSLATE);
|
||||
RNA_boolean_set(otmacro->ptr, "use_automerge_and_split", true);
|
||||
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
||||
}
|
||||
|
||||
|
|
|
@ -497,8 +497,8 @@ void ED_operatormacros_graph(void)
|
|||
"Make a copy of all selected keyframes and move them",
|
||||
OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
WM_operatortype_macro_define(ot, "GRAPH_OT_duplicate");
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_transform");
|
||||
RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_DUPLICATE);
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
||||
RNA_boolean_set(otmacro->ptr, "use_automerge_and_split", true);
|
||||
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ set(SRC
|
|||
tree/tree_element_driver.cc
|
||||
tree/tree_element_gpencil_layer.cc
|
||||
tree/tree_element_id.cc
|
||||
tree/tree_element_id_curve.cc
|
||||
tree/tree_element_id_library.cc
|
||||
tree/tree_element_id_mesh.cc
|
||||
tree/tree_element_id_scene.cc
|
||||
|
@ -78,6 +79,7 @@ set(SRC
|
|||
tree/tree_element_driver.hh
|
||||
tree/tree_element_gpencil_layer.hh
|
||||
tree/tree_element_id.hh
|
||||
tree/tree_element_id_curve.hh
|
||||
tree/tree_element_id_library.hh
|
||||
tree/tree_element_id_mesh.hh
|
||||
tree/tree_element_id_scene.hh
|
||||
|
|
|
@ -74,6 +74,18 @@ static void outliner_show_active(SpaceOutliner *space_outliner,
|
|||
TreeElement *te,
|
||||
ID *id);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Local Utilities
|
||||
* \{ */
|
||||
|
||||
static void outliner_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
|
||||
{
|
||||
/* NOTE: this uses the same path as the 3D viewport. */
|
||||
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer.blend");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Highlight on Cursor Motion Operator
|
||||
* \{ */
|
||||
|
@ -817,7 +829,7 @@ static int outliner_id_copy_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
|
||||
outliner_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
BKE_copybuffer_copy_end(bmain, filepath, op->reports);
|
||||
|
||||
BKE_reportf(op->reports, RPT_INFO, "Copied %d selected data-block(s)", num_ids);
|
||||
|
@ -851,7 +863,7 @@ static int outliner_id_paste_exec(bContext *C, wmOperator *op)
|
|||
char filepath[FILE_MAX];
|
||||
const short flag = FILE_AUTOSELECT | FILE_ACTIVE_COLLECTION;
|
||||
|
||||
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
|
||||
outliner_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
|
||||
const int num_pasted = BKE_copybuffer_paste(C, filepath, flag, op->reports, 0);
|
||||
if (num_pasted == 0) {
|
||||
|
|
|
@ -552,24 +552,13 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
|
|||
case ID_LI:
|
||||
case ID_SCE:
|
||||
case ID_ME:
|
||||
case ID_CU_LEGACY:
|
||||
BLI_assert_msg(0, "ID type expected to be expanded through new tree-element design");
|
||||
break;
|
||||
case ID_OB: {
|
||||
outliner_add_object_contents(space_outliner, te, tselem, (Object *)id);
|
||||
break;
|
||||
}
|
||||
case ID_CU_LEGACY: {
|
||||
Curve *cu = (Curve *)id;
|
||||
|
||||
if (outliner_animdata_test(cu->adt)) {
|
||||
outliner_add_element(space_outliner, &te->subtree, cu, te, TSE_ANIM_DATA, 0);
|
||||
}
|
||||
|
||||
for (int a = 0; a < cu->totcol; a++) {
|
||||
outliner_add_element(space_outliner, &te->subtree, cu->mat[a], te, TSE_SOME_ID, a);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ID_MB: {
|
||||
MetaBall *mb = (MetaBall *)id;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "../outliner_intern.hh"
|
||||
#include "common.hh"
|
||||
#include "tree_element_id_curve.hh"
|
||||
#include "tree_element_id_library.hh"
|
||||
#include "tree_element_id_mesh.hh"
|
||||
#include "tree_element_id_scene.hh"
|
||||
|
@ -42,8 +43,9 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
|
|||
return std::make_unique<TreeElementIDScene>(legacy_te, (Scene &)id);
|
||||
case ID_ME:
|
||||
return std::make_unique<TreeElementIDMesh>(legacy_te, (Mesh &)id);
|
||||
case ID_OB:
|
||||
case ID_CU_LEGACY:
|
||||
return std::make_unique<TreeElementIDCurve>(legacy_te, (Curve &)id);
|
||||
case ID_OB:
|
||||
case ID_MB:
|
||||
case ID_MA:
|
||||
case ID_TE:
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_outliner_types.h"
|
||||
|
||||
#include "../outliner_intern.hh"
|
||||
|
||||
#include "tree_element_id_curve.hh"
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
TreeElementIDCurve::TreeElementIDCurve(TreeElement &legacy_te, Curve &curve)
|
||||
: TreeElementID(legacy_te, curve.id), curve_(curve)
|
||||
{
|
||||
}
|
||||
|
||||
bool TreeElementIDCurve::isExpandValid() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void TreeElementIDCurve::expand(SpaceOutliner &space_outliner) const
|
||||
{
|
||||
expand_animation_data(space_outliner, curve_.adt);
|
||||
|
||||
expandMaterials(space_outliner);
|
||||
}
|
||||
|
||||
void TreeElementIDCurve::expandMaterials(SpaceOutliner &space_outliner) const
|
||||
{
|
||||
for (int a = 0; a < curve_.totcol; a++) {
|
||||
outliner_add_element(
|
||||
&space_outliner, &legacy_te_.subtree, curve_.mat[a], &legacy_te_, TSE_SOME_ID, a);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::outliner
|
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tree_element_id.hh"
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
class TreeElementIDCurve final : public TreeElementID {
|
||||
Curve &curve_;
|
||||
|
||||
public:
|
||||
TreeElementIDCurve(TreeElement &legacy_te, Curve &curve);
|
||||
|
||||
void expand(SpaceOutliner &) const override;
|
||||
bool isExpandValid() const override;
|
||||
|
||||
private:
|
||||
void expandMaterials(SpaceOutliner &) const;
|
||||
};
|
||||
|
||||
} // namespace blender::ed::outliner
|
|
@ -951,11 +951,11 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
|
|||
if ((len < (numstr_size[1] * 2.5f)) ||
|
||||
((len < (numstr_size[0] + bg_margin + bg_margin)) && (fabs(rot_90_vec[0]) < 0.5f)))
|
||||
{
|
||||
/* Super short, or quite short and also shallow angle. Position below line.*/
|
||||
/* Super short, or quite short and also shallow angle. Position below line. */
|
||||
posit[1] = MIN2(co_ss[0][1], co_ss[2][1]) - numstr_size[1] - bg_margin - bg_margin;
|
||||
}
|
||||
else if (fabs(rot_90_vec[0]) < 0.2f) {
|
||||
/* Very shallow angle. Shift down by text height.*/
|
||||
/* Very shallow angle. Shift down by text height. */
|
||||
posit[1] -= numstr_size[1];
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,17 @@
|
|||
# include "BLI_math_base.h" /* M_PI */
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Local Utilities
|
||||
* \{ */
|
||||
|
||||
static void view3d_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
|
||||
{
|
||||
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer.blend");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ************************** copy paste ***************************** */
|
||||
|
||||
static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
|
||||
|
@ -60,7 +71,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
|
||||
view3d_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
BKE_copybuffer_copy_end(bmain, filepath, op->reports);
|
||||
|
||||
BKE_reportf(op->reports, RPT_INFO, "Copied %d selected object(s)", num_copied);
|
||||
|
@ -92,7 +103,7 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op)
|
|||
flag |= FILE_ACTIVE_COLLECTION;
|
||||
}
|
||||
|
||||
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
|
||||
view3d_copybuffer_filepath_get(filepath, sizeof(filepath));
|
||||
|
||||
const int num_pasted = BKE_copybuffer_paste(C, filepath, flag, op->reports, FILTER_ID_OB);
|
||||
if (num_pasted == 0) {
|
||||
|
|
|
@ -782,7 +782,7 @@ static void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
|
|||
bAnimContext ac;
|
||||
|
||||
const bool canceled = (t->state == TRANS_CANCEL);
|
||||
const bool duplicate = (t->mode == TFM_TIME_DUPLICATE);
|
||||
const bool duplicate = (t->flag & T_AUTOMERGE) != 0;
|
||||
|
||||
/* initialize relevant anim-context 'context' data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
|
|
|
@ -136,7 +136,7 @@ static void bezt_to_transdata(TransData *td,
|
|||
|
||||
static bool graph_edit_is_translation_mode(TransInfo *t)
|
||||
{
|
||||
return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE);
|
||||
return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE);
|
||||
}
|
||||
|
||||
static bool graph_edit_use_local_center(TransInfo *t)
|
||||
|
@ -983,7 +983,7 @@ static void special_aftertrans_update__graph(bContext *C, TransInfo *t)
|
|||
const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
|
||||
|
||||
const bool canceled = (t->state == TRANS_CANCEL);
|
||||
const bool duplicate = (t->mode == TFM_TIME_DUPLICATE);
|
||||
const bool duplicate = (t->flag & T_AUTOMERGE) != 0;
|
||||
|
||||
/* initialize relevant anim-context 'context' data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
|
|
|
@ -630,21 +630,19 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
|
|||
t->options |= CTX_NO_PET;
|
||||
}
|
||||
|
||||
if (t->obedit_type == OB_MESH) {
|
||||
if (op && (prop = RNA_struct_find_property(op->ptr, "use_automerge_and_split")) &&
|
||||
RNA_property_is_set(op->ptr, prop))
|
||||
{
|
||||
if (RNA_property_boolean_get(op->ptr, prop)) {
|
||||
t->flag |= T_AUTOMERGE | T_AUTOSPLIT;
|
||||
}
|
||||
if (op && (prop = RNA_struct_find_property(op->ptr, "use_automerge_and_split")) &&
|
||||
RNA_property_is_set(op->ptr, prop))
|
||||
{
|
||||
if (RNA_property_boolean_get(op->ptr, prop)) {
|
||||
t->flag |= T_AUTOMERGE | T_AUTOSPLIT;
|
||||
}
|
||||
else {
|
||||
char automerge = t->scene->toolsettings->automerge;
|
||||
if (automerge & AUTO_MERGE) {
|
||||
t->flag |= T_AUTOMERGE;
|
||||
if (automerge & AUTO_MERGE_AND_SPLIT) {
|
||||
t->flag |= T_AUTOSPLIT;
|
||||
}
|
||||
}
|
||||
else if (t->obedit_type == OB_MESH) {
|
||||
char automerge = t->scene->toolsettings->automerge;
|
||||
if (automerge & AUTO_MERGE) {
|
||||
t->flag |= T_AUTOMERGE;
|
||||
if (automerge & AUTO_MERGE_AND_SPLIT) {
|
||||
t->flag |= T_AUTOSPLIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1167,17 +1167,6 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
|
|||
case TFM_TIME_SCALE:
|
||||
initTimeScale(t);
|
||||
break;
|
||||
case TFM_TIME_DUPLICATE:
|
||||
/* same as TFM_TIME_EXTEND, but we need the mode info for later
|
||||
* so that duplicate-culling will work properly
|
||||
*/
|
||||
if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
|
||||
initTranslation(t);
|
||||
}
|
||||
else {
|
||||
initTimeTranslate(t);
|
||||
}
|
||||
break;
|
||||
case TFM_TIME_EXTEND:
|
||||
/* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
|
||||
* Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "BLI_task.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_unit.h"
|
||||
|
||||
#include "ED_screen.h"
|
||||
|
@ -171,34 +172,31 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
|
|||
|
||||
void initShrinkFatten(TransInfo *t)
|
||||
{
|
||||
/* If not in mesh edit mode, fallback to Resize. */
|
||||
if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) {
|
||||
float no_mouse_dir_constraint[3];
|
||||
zero_v3(no_mouse_dir_constraint);
|
||||
initResize(t, no_mouse_dir_constraint);
|
||||
BKE_report(t->reports, RPT_ERROR, "'Shrink/Fatten' meshes is only supported in edit mode");
|
||||
t->state = TRANS_CANCEL;
|
||||
}
|
||||
else {
|
||||
t->mode = TFM_SHRINKFATTEN;
|
||||
t->transform = applyShrinkFatten;
|
||||
t->handleEvent = shrinkfatten_handleEvent;
|
||||
|
||||
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
|
||||
t->mode = TFM_SHRINKFATTEN;
|
||||
t->transform = applyShrinkFatten;
|
||||
t->handleEvent = shrinkfatten_handleEvent;
|
||||
|
||||
t->idx_max = 0;
|
||||
t->num.idx_max = 0;
|
||||
t->snap[0] = 1.0f;
|
||||
t->snap[1] = t->snap[0] * 0.1f;
|
||||
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
|
||||
|
||||
copy_v3_fl(t->num.val_inc, t->snap[0]);
|
||||
t->num.unit_sys = t->scene->unit.system;
|
||||
t->num.unit_type[0] = B_UNIT_LENGTH;
|
||||
t->idx_max = 0;
|
||||
t->num.idx_max = 0;
|
||||
t->snap[0] = 1.0f;
|
||||
t->snap[1] = t->snap[0] * 0.1f;
|
||||
|
||||
t->flag |= T_NO_CONSTRAINT;
|
||||
copy_v3_fl(t->num.val_inc, t->snap[0]);
|
||||
t->num.unit_sys = t->scene->unit.system;
|
||||
t->num.unit_type[0] = B_UNIT_LENGTH;
|
||||
|
||||
if (t->keymap) {
|
||||
/* Workaround to use the same key as the modal keymap. */
|
||||
t->custom.mode.data = (void *)WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_RESIZE);
|
||||
}
|
||||
t->flag |= T_NO_CONSTRAINT;
|
||||
|
||||
if (t->keymap) {
|
||||
/* Workaround to use the same key as the modal keymap. */
|
||||
t->custom.mode.data = (void *)WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_RESIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1380,7 +1380,8 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot)
|
|||
|
||||
Transform_Properties(ot,
|
||||
P_ORIENT_AXIS | P_ORIENT_MATRIX | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR |
|
||||
P_ALIGN_SNAP | P_GPENCIL_EDIT | P_CENTER | P_VIEW3D_NAVIGATION);
|
||||
P_ALIGN_SNAP | P_GPENCIL_EDIT | P_CENTER | P_VIEW3D_NAVIGATION |
|
||||
P_POST_TRANSFORM);
|
||||
}
|
||||
|
||||
static int transform_from_gizmo_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
||||
|
|
|
@ -67,6 +67,17 @@ static bool lib_id_preview_editing_poll(bContext *C)
|
|||
return true;
|
||||
}
|
||||
|
||||
static ID *lib_id_load_custom_preview_id_get(bContext *C, const wmOperator *op)
|
||||
{
|
||||
/* #invoke() gets the ID from context and saves it in the custom data. */
|
||||
if (op->customdata) {
|
||||
return static_cast<ID *>(op->customdata);
|
||||
}
|
||||
|
||||
PointerRNA idptr = CTX_data_pointer_get(C, "id");
|
||||
return static_cast<ID *>(idptr.data);
|
||||
}
|
||||
|
||||
static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
char filepath[FILE_MAX];
|
||||
|
@ -78,8 +89,12 @@ static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
PointerRNA idptr = CTX_data_pointer_get(C, "id");
|
||||
ID *id = (ID *)idptr.data;
|
||||
ID *id = lib_id_load_custom_preview_id_get(C, op);
|
||||
if (!id) {
|
||||
BKE_report(
|
||||
op->reports, RPT_ERROR, "Failed to set preview: no ID in context (incorrect context?)");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BKE_previewimg_id_custom_set(id, filepath);
|
||||
|
||||
|
@ -88,6 +103,18 @@ static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the ID from context, and spawn a File Browser to select the preview image. The
|
||||
* File Browser may re-use the Asset Browser under the cursor, and clear the file-list on
|
||||
* confirmation, leading to failure to obtain the ID at that point. So get it before spawning the
|
||||
* File Browser (store it in the operator custom data).
|
||||
*/
|
||||
static int lib_id_load_custom_preview_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
op->customdata = lib_id_load_custom_preview_id_get(C, op);
|
||||
return WM_operator_filesel(C, op, event);
|
||||
}
|
||||
|
||||
static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Load Custom Preview";
|
||||
|
@ -97,7 +124,7 @@ static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
|
|||
/* api callbacks */
|
||||
ot->poll = lib_id_preview_editing_poll;
|
||||
ot->exec = lib_id_load_custom_preview_exec;
|
||||
ot->invoke = WM_operator_filesel;
|
||||
ot->invoke = lib_id_load_custom_preview_invoke;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
|
|
|
@ -67,11 +67,12 @@ bool LazyFunction::always_used_inputs_available(const Params ¶ms) const
|
|||
|
||||
void Params::set_default_remaining_outputs()
|
||||
{
|
||||
for (const int i : fn_.outputs().index_range()) {
|
||||
const Span<Output> outputs = fn_.outputs();
|
||||
for (const int i : outputs.index_range()) {
|
||||
if (this->output_was_set(i)) {
|
||||
continue;
|
||||
}
|
||||
const Output &fn_output = fn_.outputs()[i];
|
||||
const Output &fn_output = outputs[i];
|
||||
const CPPType &type = *fn_output.type;
|
||||
void *data_ptr = this->get_output_data_ptr(i);
|
||||
type.value_initialize(data_ptr);
|
||||
|
|
|
@ -19,6 +19,7 @@ set(SRC
|
|||
intern/add_curves_on_mesh.cc
|
||||
intern/curve_constraints.cc
|
||||
intern/fillet_curves.cc
|
||||
intern/mesh_copy_selection.cc
|
||||
intern/mesh_flip_faces.cc
|
||||
intern/mesh_merge_by_distance.cc
|
||||
intern/mesh_primitive_cuboid.cc
|
||||
|
@ -43,6 +44,7 @@ set(SRC
|
|||
GEO_mesh_merge_by_distance.hh
|
||||
GEO_mesh_primitive_cuboid.hh
|
||||
GEO_mesh_split_edges.hh
|
||||
GEO_mesh_copy_selection.hh
|
||||
GEO_mesh_to_curve.hh
|
||||
GEO_mesh_to_volume.hh
|
||||
GEO_point_merge_by_distance.hh
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
struct Mesh;
|
||||
namespace blender {
|
||||
namespace fn {
|
||||
template<typename T> class Field;
|
||||
}
|
||||
namespace bke {
|
||||
class AnonymousAttributePropagationInfo;
|
||||
}
|
||||
} // namespace blender
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
std::optional<Mesh *> mesh_copy_selection(
|
||||
const Mesh &src_mesh,
|
||||
const fn::Field<bool> &selection,
|
||||
eAttrDomain selection_domain,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
std::optional<Mesh *> mesh_copy_selection_keep_verts(
|
||||
const Mesh &src_mesh,
|
||||
const fn::Field<bool> &selection,
|
||||
eAttrDomain selection_domain,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
std::optional<Mesh *> mesh_copy_selection_keep_edges(
|
||||
const Mesh &mesh,
|
||||
const fn::Field<bool> &selection,
|
||||
eAttrDomain selection_domain,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
|
@ -0,0 +1,520 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_geometry_fields.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "GEO_mesh_copy_selection.hh"
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
static void create_reverse_map(const IndexMask &mask, MutableSpan<int> r_map)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
r_map.fill(-1);
|
||||
#endif
|
||||
mask.foreach_index_optimized<int>(
|
||||
GrainSize(4096), [&](const int src_i, const int dst_i) { r_map[src_i] = dst_i; });
|
||||
}
|
||||
|
||||
static void remap_verts(const OffsetIndices<int> src_polys,
|
||||
const OffsetIndices<int> dst_polys,
|
||||
const int src_verts_num,
|
||||
const IndexMask &vert_mask,
|
||||
const IndexMask &edge_mask,
|
||||
const IndexMask &poly_mask,
|
||||
const Span<int2> src_edges,
|
||||
const Span<int> src_corner_verts,
|
||||
MutableSpan<int2> dst_edges,
|
||||
MutableSpan<int> dst_corner_verts)
|
||||
{
|
||||
Array<int> map(src_verts_num);
|
||||
create_reverse_map(vert_mask, map);
|
||||
threading::parallel_invoke(
|
||||
vert_mask.size() > 1024,
|
||||
[&]() {
|
||||
poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||
const IndexRange src_poly = src_polys[src_i];
|
||||
const IndexRange dst_poly = dst_polys[dst_i];
|
||||
for (const int i : src_poly.index_range()) {
|
||||
dst_corner_verts[dst_poly[i]] = map[src_corner_verts[src_poly[i]]];
|
||||
}
|
||||
});
|
||||
},
|
||||
[&]() {
|
||||
edge_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||
dst_edges[dst_i][0] = map[src_edges[src_i][0]];
|
||||
dst_edges[dst_i][1] = map[src_edges[src_i][1]];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static void remap_edges(const OffsetIndices<int> src_polys,
|
||||
const OffsetIndices<int> dst_polys,
|
||||
const int src_edges_num,
|
||||
const IndexMask &edge_mask,
|
||||
const IndexMask &poly_mask,
|
||||
const Span<int> src_corner_edges,
|
||||
MutableSpan<int> dst_corner_edges)
|
||||
{
|
||||
Array<int> map(src_edges_num);
|
||||
create_reverse_map(edge_mask, map);
|
||||
poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||
const IndexRange src_poly = src_polys[src_i];
|
||||
const IndexRange dst_poly = dst_polys[dst_i];
|
||||
for (const int i : src_poly.index_range()) {
|
||||
dst_corner_edges[dst_poly[i]] = map[src_corner_edges[src_poly[i]]];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** A vertex is selected if it's used by a selected edge. */
|
||||
static IndexMask vert_selection_from_edge(const Span<int2> edges,
|
||||
const IndexMask &edge_mask,
|
||||
const int verts_num,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
Array<bool> array(verts_num, false);
|
||||
edge_mask.foreach_index_optimized<int>([&](const int i) {
|
||||
array[edges[i][0]] = true;
|
||||
array[edges[i][1]] = true;
|
||||
});
|
||||
return IndexMask::from_bools(array, memory);
|
||||
}
|
||||
|
||||
static IndexMask mapped_corner_selection_from_poly(const OffsetIndices<int> polys,
|
||||
const IndexMask &poly_mask,
|
||||
const Span<int> corner_verts_or_edges,
|
||||
const int verts_or_edges_num,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
Array<bool> array(verts_or_edges_num, false);
|
||||
poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) {
|
||||
array.as_mutable_span().fill_indices(corner_verts_or_edges.slice(polys[i]), true);
|
||||
});
|
||||
return IndexMask::from_bools(array, memory);
|
||||
}
|
||||
|
||||
/** A vertex is selected if it is used by a selected face. */
|
||||
static IndexMask vert_selection_from_poly(const OffsetIndices<int> polys,
|
||||
const IndexMask &poly_mask,
|
||||
const Span<int> corner_verts,
|
||||
const int verts_num,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return mapped_corner_selection_from_poly(polys, poly_mask, corner_verts, verts_num, memory);
|
||||
}
|
||||
|
||||
/** An edge is selected if it is used by a selected face. */
|
||||
static IndexMask edge_selection_from_poly(const OffsetIndices<int> polys,
|
||||
const IndexMask &poly_mask,
|
||||
const Span<int> corner_edges,
|
||||
const int edges_num,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return mapped_corner_selection_from_poly(polys, poly_mask, corner_edges, edges_num, memory);
|
||||
}
|
||||
|
||||
/** An edge is selected if both of its vertices are selected. */
|
||||
static IndexMask edge_selection_from_vert(const Span<int2> edges,
|
||||
const Span<bool> vert_selection,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return IndexMask::from_predicate(
|
||||
edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) {
|
||||
const int2 edge = edges[i];
|
||||
return vert_selection[edge[0]] && vert_selection[edge[1]];
|
||||
});
|
||||
}
|
||||
|
||||
static IndexMask poly_selection_from_mapped_corner(const OffsetIndices<int> polys,
|
||||
const Span<int> corner_verts_or_edges,
|
||||
const Span<bool> vert_or_edge_selection,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return IndexMask::from_predicate(
|
||||
polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) {
|
||||
const Span<int> indices = corner_verts_or_edges.slice(polys[i]);
|
||||
return std::all_of(indices.begin(), indices.end(), [&](const int i) {
|
||||
return vert_or_edge_selection[i];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** A face is selected if all of its vertices are selected. */
|
||||
static IndexMask poly_selection_from_vert(const OffsetIndices<int> polys,
|
||||
const Span<int> corner_verts,
|
||||
const Span<bool> vert_selection,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory);
|
||||
}
|
||||
|
||||
/** A face is selected if all of its edges are selected. */
|
||||
static IndexMask poly_selection_from_edge(const OffsetIndices<int> polys,
|
||||
const Span<int> corner_edges,
|
||||
const Span<bool> edge_mask,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return poly_selection_from_mapped_corner(polys, corner_edges, edge_mask, memory);
|
||||
}
|
||||
|
||||
/** Create a mesh with no built-in attributes. */
|
||||
static Mesh *create_mesh_no_attributes(const Mesh ¶ms_mesh,
|
||||
const int verts_num,
|
||||
const int edges_num,
|
||||
const int polys_num,
|
||||
const int corners_num)
|
||||
{
|
||||
Mesh *mesh = BKE_mesh_new_nomain(0, 0, polys_num, 0);
|
||||
mesh->totvert = verts_num;
|
||||
mesh->totedge = edges_num;
|
||||
mesh->totloop = corners_num;
|
||||
CustomData_free_layer_named(&mesh->vdata, "position", 0);
|
||||
CustomData_free_layer_named(&mesh->edata, ".edge_verts", 0);
|
||||
CustomData_free_layer_named(&mesh->ldata, ".corner_vert", 0);
|
||||
CustomData_free_layer_named(&mesh->ldata, ".corner_edge", 0);
|
||||
BKE_mesh_copy_parameters_for_eval(mesh, ¶ms_mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void copy_loose_vert_hint(const Mesh &src, Mesh &dst)
|
||||
{
|
||||
const auto &src_cache = src.runtime->loose_verts_cache;
|
||||
if (src_cache.is_cached() && src_cache.data().count == 0) {
|
||||
dst.tag_loose_verts_none();
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_loose_edge_hint(const Mesh &src, Mesh &dst)
|
||||
{
|
||||
const auto &src_cache = src.runtime->loose_edges_cache;
|
||||
if (src_cache.is_cached() && src_cache.data().count == 0) {
|
||||
dst.tag_loose_edges_none();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Mesh *> mesh_copy_selection(
|
||||
const Mesh &src_mesh,
|
||||
const fn::Field<bool> &selection_field,
|
||||
const eAttrDomain selection_domain,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const Span<int2> src_edges = src_mesh.edges();
|
||||
const OffsetIndices src_polys = src_mesh.polys();
|
||||
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
||||
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||
|
||||
const bke::MeshFieldContext context(src_mesh, selection_domain);
|
||||
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
|
||||
evaluator.add(selection_field);
|
||||
evaluator.evaluate();
|
||||
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||
if (selection.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (const std::optional<bool> single = selection.get_if_single()) {
|
||||
return *single ? std::nullopt : std::make_optional<Mesh *>(nullptr);
|
||||
}
|
||||
|
||||
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
|
||||
IndexMask vert_mask;
|
||||
IndexMask edge_mask;
|
||||
IndexMask poly_mask;
|
||||
switch (selection_domain) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
const VArraySpan<bool> span(selection);
|
||||
threading::parallel_invoke(
|
||||
src_mesh.totvert > 1024,
|
||||
[&]() { vert_mask = IndexMask::from_bools(span, memory.local()); },
|
||||
[&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
|
||||
[&]() {
|
||||
poly_mask = poly_selection_from_vert(
|
||||
src_polys, src_corner_verts, span, memory.local());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_EDGE: {
|
||||
const VArraySpan<bool> span(selection);
|
||||
threading::parallel_invoke(
|
||||
src_edges.size() > 1024,
|
||||
[&]() {
|
||||
edge_mask = IndexMask::from_bools(span, memory.local());
|
||||
vert_mask = vert_selection_from_edge(
|
||||
src_edges, edge_mask, src_mesh.totvert, memory.local());
|
||||
},
|
||||
[&]() {
|
||||
poly_mask = poly_selection_from_edge(
|
||||
src_polys, src_corner_edges, span, memory.local());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
const VArraySpan<bool> span(selection);
|
||||
poly_mask = IndexMask::from_bools(span, memory.local());
|
||||
threading::parallel_invoke(
|
||||
poly_mask.size() > 1024,
|
||||
[&]() {
|
||||
vert_mask = vert_selection_from_poly(
|
||||
src_polys, poly_mask, src_corner_verts, src_mesh.totvert, memory.local());
|
||||
},
|
||||
[&]() {
|
||||
edge_mask = edge_selection_from_poly(
|
||||
src_polys, poly_mask, src_corner_edges, src_mesh.totedge, memory.local());
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
if (vert_mask.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (vert_mask.size() == src_mesh.totvert) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Mesh *dst_mesh = create_mesh_no_attributes(
|
||||
src_mesh, vert_mask.size(), edge_mask.size(), poly_mask.size(), 0);
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
||||
dst_attributes.add<int2>(".edge_verts", ATTR_DOMAIN_EDGE, bke::AttributeInitConstruct());
|
||||
MutableSpan<int2> dst_edges = dst_mesh->edges_for_write();
|
||||
|
||||
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
||||
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
|
||||
dst_mesh->totloop = dst_polys.total_size();
|
||||
dst_attributes.add<int>(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
||||
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
||||
MutableSpan<int> dst_corner_verts = dst_mesh->corner_verts_for_write();
|
||||
MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
|
||||
|
||||
threading::parallel_invoke(
|
||||
vert_mask.size() > 1024,
|
||||
[&]() {
|
||||
remap_verts(src_polys,
|
||||
dst_polys,
|
||||
src_mesh.totvert,
|
||||
vert_mask,
|
||||
edge_mask,
|
||||
poly_mask,
|
||||
src_edges,
|
||||
src_corner_verts,
|
||||
dst_edges,
|
||||
dst_corner_verts);
|
||||
},
|
||||
[&]() {
|
||||
remap_edges(src_polys,
|
||||
dst_polys,
|
||||
src_edges.size(),
|
||||
edge_mask,
|
||||
poly_mask,
|
||||
src_corner_edges,
|
||||
dst_corner_edges);
|
||||
},
|
||||
[&]() {
|
||||
bke::gather_attributes(
|
||||
src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, vert_mask, dst_attributes);
|
||||
bke::gather_attributes(src_attributes,
|
||||
ATTR_DOMAIN_EDGE,
|
||||
propagation_info,
|
||||
{".edge_verts"},
|
||||
edge_mask,
|
||||
dst_attributes);
|
||||
bke::gather_attributes(
|
||||
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
|
||||
bke::gather_attributes_group_to_group(src_attributes,
|
||||
ATTR_DOMAIN_CORNER,
|
||||
propagation_info,
|
||||
{".corner_edge", ".corner_vert"},
|
||||
src_polys,
|
||||
dst_polys,
|
||||
poly_mask,
|
||||
dst_attributes);
|
||||
});
|
||||
|
||||
if (selection_domain == ATTR_DOMAIN_EDGE) {
|
||||
copy_loose_vert_hint(src_mesh, *dst_mesh);
|
||||
}
|
||||
else if (selection_domain == ATTR_DOMAIN_FACE) {
|
||||
copy_loose_vert_hint(src_mesh, *dst_mesh);
|
||||
copy_loose_edge_hint(src_mesh, *dst_mesh);
|
||||
}
|
||||
|
||||
return dst_mesh;
|
||||
}
|
||||
|
||||
std::optional<Mesh *> mesh_copy_selection_keep_verts(
|
||||
const Mesh &src_mesh,
|
||||
const fn::Field<bool> &selection_field,
|
||||
const eAttrDomain selection_domain,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const Span<int2> src_edges = src_mesh.edges();
|
||||
const OffsetIndices src_polys = src_mesh.polys();
|
||||
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
||||
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||
|
||||
const bke::MeshFieldContext context(src_mesh, selection_domain);
|
||||
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
|
||||
evaluator.add(selection_field);
|
||||
evaluator.evaluate();
|
||||
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||
if (selection.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
|
||||
IndexMask edge_mask;
|
||||
IndexMask poly_mask;
|
||||
switch (selection_domain) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
const VArraySpan<bool> span(selection);
|
||||
threading::parallel_invoke(
|
||||
src_edges.size() > 1024,
|
||||
[&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
|
||||
[&]() {
|
||||
poly_mask = poly_selection_from_vert(
|
||||
src_polys, src_corner_verts, span, memory.local());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_EDGE: {
|
||||
const VArraySpan<bool> span(selection);
|
||||
threading::parallel_invoke(
|
||||
src_edges.size() > 1024,
|
||||
[&]() { edge_mask = IndexMask::from_bools(span, memory.local()); },
|
||||
[&]() {
|
||||
poly_mask = poly_selection_from_edge(
|
||||
src_polys, src_corner_edges, span, memory.local());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
const VArraySpan<bool> span(selection);
|
||||
poly_mask = IndexMask::from_bools(span, memory.local());
|
||||
edge_mask = edge_selection_from_poly(
|
||||
src_polys, poly_mask, src_corner_edges, src_edges.size(), memory.local());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
Mesh *dst_mesh = create_mesh_no_attributes(
|
||||
src_mesh, src_mesh.totvert, edge_mask.size(), poly_mask.size(), 0);
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
||||
|
||||
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
||||
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
|
||||
dst_mesh->totloop = dst_polys.total_size();
|
||||
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
||||
MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
|
||||
|
||||
threading::parallel_invoke(
|
||||
[&]() {
|
||||
remap_edges(src_polys,
|
||||
dst_polys,
|
||||
src_edges.size(),
|
||||
edge_mask,
|
||||
poly_mask,
|
||||
src_corner_edges,
|
||||
dst_corner_edges);
|
||||
},
|
||||
[&]() {
|
||||
bke::copy_attributes(
|
||||
src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
|
||||
bke::gather_attributes(
|
||||
src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, edge_mask, dst_attributes);
|
||||
bke::gather_attributes(
|
||||
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
|
||||
bke::gather_attributes_group_to_group(src_attributes,
|
||||
ATTR_DOMAIN_CORNER,
|
||||
propagation_info,
|
||||
{".corner_edge"},
|
||||
src_polys,
|
||||
dst_polys,
|
||||
poly_mask,
|
||||
dst_attributes);
|
||||
});
|
||||
|
||||
/* Positions are not changed by the operation, so the bounds are the same. */
|
||||
dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
|
||||
copy_loose_vert_hint(src_mesh, *dst_mesh);
|
||||
return dst_mesh;
|
||||
}
|
||||
|
||||
std::optional<Mesh *> mesh_copy_selection_keep_edges(
|
||||
const Mesh &src_mesh,
|
||||
const fn::Field<bool> &selection_field,
|
||||
const eAttrDomain selection_domain,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const OffsetIndices src_polys = src_mesh.polys();
|
||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||
|
||||
const bke::MeshFieldContext context(src_mesh, selection_domain);
|
||||
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
|
||||
evaluator.add(selection_field);
|
||||
evaluator.evaluate();
|
||||
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||
if (selection.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
IndexMaskMemory memory;
|
||||
IndexMask poly_mask;
|
||||
switch (selection_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
poly_mask = poly_selection_from_vert(
|
||||
src_polys, src_mesh.corner_verts(), VArraySpan(selection), memory);
|
||||
break;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
poly_mask = poly_selection_from_edge(
|
||||
src_polys, src_mesh.corner_edges(), VArraySpan(selection), memory);
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
poly_mask = IndexMask::from_bools(selection, memory);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
Mesh *dst_mesh = create_mesh_no_attributes(
|
||||
src_mesh, src_mesh.totvert, src_mesh.totedge, poly_mask.size(), 0);
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
||||
|
||||
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
||||
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
|
||||
dst_mesh->totloop = dst_polys.total_size();
|
||||
dst_attributes.add<int>(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
||||
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
||||
|
||||
bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
|
||||
bke::copy_attributes(src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, dst_attributes);
|
||||
bke::gather_attributes(
|
||||
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
|
||||
bke::gather_attributes_group_to_group(src_attributes,
|
||||
ATTR_DOMAIN_CORNER,
|
||||
propagation_info,
|
||||
{},
|
||||
src_polys,
|
||||
dst_polys,
|
||||
poly_mask,
|
||||
dst_attributes);
|
||||
|
||||
/* Positions are not changed by the operation, so the bounds are the same. */
|
||||
dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
|
||||
copy_loose_vert_hint(src_mesh, *dst_mesh);
|
||||
return dst_mesh;
|
||||
}
|
||||
|
||||
} // namespace blender::geometry
|
|
@ -185,7 +185,7 @@ static bool can_rotate(const Span<PackIsland *> islands, const UVPackIsland_Para
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Angle rounding helper for "D4" transforms. */
|
||||
/** Angle rounding helper for "D4" transforms. */
|
||||
static float angle_match(float angle_radians, float target_radians)
|
||||
{
|
||||
if (fabsf(angle_radians - target_radians) < DEG2RADF(0.1f)) {
|
||||
|
@ -194,7 +194,7 @@ static float angle_match(float angle_radians, float target_radians)
|
|||
return angle_radians;
|
||||
}
|
||||
|
||||
/** Angle rounding helper for "D4" transforms. */
|
||||
/** Angle rounding helper for "D4" transforms. */
|
||||
static float plusminus_90_angle(float angle_radians)
|
||||
{
|
||||
angle_radians = angle_radians - floorf((angle_radians + M_PI_2) / M_PI) * M_PI;
|
||||
|
@ -375,7 +375,7 @@ class UVAABBIsland {
|
|||
*/
|
||||
static void pack_islands_alpaca_turbo(const int64_t exclude_index,
|
||||
const rctf &exclude,
|
||||
const Span<UVAABBIsland *> islands,
|
||||
const Span<std::unique_ptr<UVAABBIsland>> islands,
|
||||
const float target_aspect_y,
|
||||
MutableSpan<uv_phi> r_phis,
|
||||
rctf *r_extent)
|
||||
|
@ -390,9 +390,9 @@ static void pack_islands_alpaca_turbo(const int64_t exclude_index,
|
|||
|
||||
/* Visit every island in order, except the excluded islands at the start. */
|
||||
for (int64_t index = exclude_index; index < islands.size(); index++) {
|
||||
UVAABBIsland *island = islands[index];
|
||||
const float dsm_u = island->uv_diagonal.x;
|
||||
const float dsm_v = island->uv_diagonal.y;
|
||||
UVAABBIsland &island = *islands[index];
|
||||
const float dsm_u = island.uv_diagonal.x;
|
||||
const float dsm_v = island.uv_diagonal.y;
|
||||
|
||||
bool restart = false;
|
||||
if (zigzag) {
|
||||
|
@ -409,7 +409,7 @@ static void pack_islands_alpaca_turbo(const int64_t exclude_index,
|
|||
}
|
||||
|
||||
/* Place the island. */
|
||||
uv_phi &phi = r_phis[island->index];
|
||||
uv_phi &phi = r_phis[island.index];
|
||||
phi.rotation = 0.0f;
|
||||
phi.translation.x = u0 + dsm_u * 0.5f;
|
||||
phi.translation.y = v0 + dsm_v * 0.5f;
|
||||
|
@ -486,7 +486,7 @@ static void update_hole_rotate(float2 &hole,
|
|||
*/
|
||||
static void pack_islands_alpaca_rotate(const int64_t exclude_index,
|
||||
const rctf &exclude,
|
||||
const Span<UVAABBIsland *> islands,
|
||||
const Span<std::unique_ptr<UVAABBIsland>> islands,
|
||||
const float target_aspect_y,
|
||||
MutableSpan<uv_phi> r_phis,
|
||||
rctf *r_extent)
|
||||
|
@ -506,30 +506,30 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
|
|||
|
||||
/* Visit every island in order, except the excluded islands at the start. */
|
||||
for (int64_t index = exclude_index; index < islands.size(); index++) {
|
||||
UVAABBIsland *island = islands[index];
|
||||
uv_phi &phi = r_phis[island->index];
|
||||
const float uvdiag_x = island->uv_diagonal.x * island->aspect_y;
|
||||
float min_dsm = std::min(uvdiag_x, island->uv_diagonal.y);
|
||||
float max_dsm = std::max(uvdiag_x, island->uv_diagonal.y);
|
||||
UVAABBIsland &island = *islands[index];
|
||||
uv_phi &phi = r_phis[island.index];
|
||||
const float uvdiag_x = island.uv_diagonal.x * island.aspect_y;
|
||||
float min_dsm = std::min(uvdiag_x, island.uv_diagonal.y);
|
||||
float max_dsm = std::max(uvdiag_x, island.uv_diagonal.y);
|
||||
|
||||
if (min_dsm < hole_diagonal.x && max_dsm < hole_diagonal.y) {
|
||||
/* Place island in the hole. */
|
||||
if (hole_rotate == (min_dsm == island->uv_diagonal.x)) {
|
||||
if (hole_rotate == (min_dsm == island.uv_diagonal.x)) {
|
||||
phi.rotation = DEG2RADF(90.0f);
|
||||
phi.translation.x = hole[0] + island->uv_diagonal.y * 0.5f / island->aspect_y;
|
||||
phi.translation.y = hole[1] + island->uv_diagonal.x * 0.5f * island->aspect_y;
|
||||
phi.translation.x = hole[0] + island.uv_diagonal.y * 0.5f / island.aspect_y;
|
||||
phi.translation.y = hole[1] + island.uv_diagonal.x * 0.5f * island.aspect_y;
|
||||
}
|
||||
else {
|
||||
phi.rotation = 0.0f;
|
||||
phi.translation.x = hole[0] + island->uv_diagonal.x * 0.5f;
|
||||
phi.translation.y = hole[1] + island->uv_diagonal.y * 0.5f;
|
||||
phi.translation.x = hole[0] + island.uv_diagonal.x * 0.5f;
|
||||
phi.translation.y = hole[1] + island.uv_diagonal.y * 0.5f;
|
||||
}
|
||||
|
||||
/* Update space left in the hole. */
|
||||
float p[6];
|
||||
p[0] = hole[0];
|
||||
p[1] = hole[1];
|
||||
p[2] = hole[0] + (hole_rotate ? max_dsm : min_dsm) / island->aspect_y;
|
||||
p[2] = hole[0] + (hole_rotate ? max_dsm : min_dsm) / island.aspect_y;
|
||||
p[3] = hole[1] + (hole_rotate ? min_dsm : max_dsm);
|
||||
p[4] = hole[0] + (hole_rotate ? hole_diagonal.y : hole_diagonal.x);
|
||||
p[5] = hole[1] + (hole_rotate ? hole_diagonal.x : hole_diagonal.y);
|
||||
|
@ -546,7 +546,7 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
|
|||
restart = (next_v1 < v0 + min_dsm);
|
||||
}
|
||||
else {
|
||||
restart = (next_u1 < u0 + min_dsm / island->aspect_y);
|
||||
restart = (next_u1 < u0 + min_dsm / island.aspect_y);
|
||||
}
|
||||
if (restart) {
|
||||
update_hole_rotate(hole, hole_diagonal, hole_rotate, u0, v0, next_u1, next_v1);
|
||||
|
@ -559,25 +559,25 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
|
|||
/* Place the island. */
|
||||
if (zigzag == (min_dsm == uvdiag_x)) {
|
||||
phi.rotation = DEG2RADF(90.0f);
|
||||
phi.translation.x = u0 + island->uv_diagonal.y * 0.5f / island->aspect_y;
|
||||
phi.translation.y = v0 + island->uv_diagonal.x * 0.5f * island->aspect_y;
|
||||
phi.translation.x = u0 + island.uv_diagonal.y * 0.5f / island.aspect_y;
|
||||
phi.translation.y = v0 + island.uv_diagonal.x * 0.5f * island.aspect_y;
|
||||
}
|
||||
else {
|
||||
phi.rotation = 0.0f;
|
||||
phi.translation.x = u0 + island->uv_diagonal.x * 0.5f;
|
||||
phi.translation.y = v0 + island->uv_diagonal.y * 0.5f;
|
||||
phi.translation.x = u0 + island.uv_diagonal.x * 0.5f;
|
||||
phi.translation.y = v0 + island.uv_diagonal.y * 0.5f;
|
||||
}
|
||||
|
||||
/* Move according to the "Alpaca rules", with rotation. */
|
||||
if (zigzag) {
|
||||
/* Move upwards. */
|
||||
v0 += min_dsm;
|
||||
next_u1 = max_ff(next_u1, u0 + max_dsm / island->aspect_y);
|
||||
next_u1 = max_ff(next_u1, u0 + max_dsm / island.aspect_y);
|
||||
next_v1 = max_ff(next_v1, v0);
|
||||
}
|
||||
else {
|
||||
/* Move sideways. */
|
||||
u0 += min_dsm / island->aspect_y;
|
||||
u0 += min_dsm / island.aspect_y;
|
||||
next_v1 = max_ff(next_v1, v0 + max_dsm);
|
||||
next_u1 = max_ff(next_u1, u0);
|
||||
}
|
||||
|
@ -592,7 +592,7 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
|
|||
*/
|
||||
static void pack_islands_fast(const int64_t exclude_index,
|
||||
const rctf &exclude,
|
||||
const Span<UVAABBIsland *> aabbs,
|
||||
const Span<std::unique_ptr<UVAABBIsland>> aabbs,
|
||||
const bool rotate,
|
||||
const float target_aspect_y,
|
||||
MutableSpan<uv_phi> r_phis,
|
||||
|
@ -607,7 +607,7 @@ static void pack_islands_fast(const int64_t exclude_index,
|
|||
}
|
||||
|
||||
/** Frits Göbel, 1979. */
|
||||
static void pack_gobel(const Span<UVAABBIsland *> aabbs,
|
||||
static void pack_gobel(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
|
||||
const float scale,
|
||||
const int m,
|
||||
MutableSpan<uv_phi> r_phis)
|
||||
|
@ -643,7 +643,7 @@ static void pack_gobel(const Span<UVAABBIsland *> aabbs,
|
|||
}
|
||||
}
|
||||
/* Attempt to find an "Optimal" packing of the islands, e.g. assuming squares or circles. */
|
||||
static void pack_islands_optimal_pack(const Span<UVAABBIsland *> aabbs,
|
||||
static void pack_islands_optimal_pack(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
|
||||
const UVPackIsland_Params ¶ms,
|
||||
MutableSpan<uv_phi> r_phis,
|
||||
rctf *r_extent)
|
||||
|
@ -694,7 +694,7 @@ static void pack_islands_optimal_pack(const Span<UVAABBIsland *> aabbs,
|
|||
}
|
||||
|
||||
/* Wrapper around #BLI_box_pack_2d. */
|
||||
static void pack_island_box_pack_2d(const Span<UVAABBIsland *> aabbs,
|
||||
static void pack_island_box_pack_2d(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
|
||||
const UVPackIsland_Params ¶ms,
|
||||
MutableSpan<uv_phi> r_phis,
|
||||
rctf *r_extent)
|
||||
|
@ -1093,7 +1093,7 @@ class UVMinimumEnclosingSquareFinder {
|
|||
* If that square is smaller than `r_extent`, then update `r_phis` accordingly.
|
||||
* \return True if `r_phis` and `r_extent` are modified.
|
||||
*/
|
||||
static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
|
||||
static bool rotate_inside_square(const Span<std::unique_ptr<UVAABBIsland>> island_indices,
|
||||
const Span<PackIsland *> islands,
|
||||
const UVPackIsland_Params ¶ms,
|
||||
const float scale,
|
||||
|
@ -1109,7 +1109,7 @@ static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
|
|||
}
|
||||
if (params.shape_method == ED_UVPACK_SHAPE_AABB) {
|
||||
/* AABB margin calculations are not preserved under rotations. */
|
||||
if (island_indices.size() > 1) { /* Unless there's only one island...*/
|
||||
if (island_indices.size() > 1) { /* Unless there's only one island. */
|
||||
|
||||
if (params.target_aspect_y != 1.0f) {
|
||||
/* TODO: Check for possible 90 degree rotation. */
|
||||
|
@ -1146,10 +1146,10 @@ static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
|
|||
/* Now we have all the points in the correct space, compute the 2D convex hull. */
|
||||
const float(*source)[2] = reinterpret_cast<const float(*)[2]>(square_finder.points.data());
|
||||
|
||||
square_finder.indices.resize(square_finder.points.size()); /* Allocate worst-case.*/
|
||||
square_finder.indices.resize(square_finder.points.size()); /* Allocate worst-case. */
|
||||
int convex_size = BLI_convexhull_2d(
|
||||
source, int(square_finder.points.size()), square_finder.indices.data());
|
||||
square_finder.indices.resize(convex_size); /* Resize to actual size.*/
|
||||
square_finder.indices.resize(convex_size); /* Resize to actual size. */
|
||||
|
||||
/* Run the computation to find the best angle. (Slow!) */
|
||||
const float quad_180 = square_finder.update(DEG2RADF(-180.0f));
|
||||
|
@ -1194,7 +1194,7 @@ static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
|
|||
* Performance would normally be `O(n^4)`, however the occupancy
|
||||
* bitmap_radix is fixed, which gives a reduced time complexity of `O(n^3)`.
|
||||
*/
|
||||
static int64_t pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
static int64_t pack_island_xatlas(const Span<std::unique_ptr<UVAABBIsland>> island_indices,
|
||||
const Span<PackIsland *> islands,
|
||||
const float scale,
|
||||
const float margin,
|
||||
|
@ -1402,69 +1402,69 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
const bool all_can_rotate = can_rotate(islands, params);
|
||||
|
||||
/* First, copy information from our input into the AABB structure. */
|
||||
Array<UVAABBIsland *> aabbs(islands.size());
|
||||
Array<std::unique_ptr<UVAABBIsland>> aabbs(islands.size());
|
||||
for (const int64_t i : islands.index_range()) {
|
||||
PackIsland *pack_island = islands[i];
|
||||
float island_scale = scale;
|
||||
if (!pack_island->can_scale_(params)) {
|
||||
island_scale = 1.0f;
|
||||
}
|
||||
UVAABBIsland *aabb = new UVAABBIsland();
|
||||
std::unique_ptr<UVAABBIsland> aabb = std::make_unique<UVAABBIsland>();
|
||||
aabb->index = i;
|
||||
aabb->uv_diagonal.x = pack_island->half_diagonal_.x * 2 * island_scale + 2 * margin;
|
||||
aabb->uv_diagonal.y = pack_island->half_diagonal_.y * 2 * island_scale + 2 * margin;
|
||||
aabb->aspect_y = pack_island->aspect_y;
|
||||
aabbs[i] = aabb;
|
||||
aabbs[i] = std::move(aabb);
|
||||
}
|
||||
|
||||
/* Sort from "biggest" to "smallest". */
|
||||
|
||||
if (all_can_rotate) {
|
||||
std::stable_sort(aabbs.begin(),
|
||||
aabbs.end(),
|
||||
[¶ms, &islands](const UVAABBIsland *a, const UVAABBIsland *b) {
|
||||
const bool can_translate_a = islands[a->index]->can_translate_(params);
|
||||
const bool can_translate_b = islands[b->index]->can_translate_(params);
|
||||
if (can_translate_a != can_translate_b) {
|
||||
return can_translate_b; /* Locked islands are placed first. */
|
||||
}
|
||||
/* TODO: Fix when (params.target_aspect_y != 1.0f) */
|
||||
std::stable_sort(
|
||||
aabbs.begin(),
|
||||
aabbs.end(),
|
||||
[&](const std::unique_ptr<UVAABBIsland> &a, const std::unique_ptr<UVAABBIsland> &b) {
|
||||
const bool can_translate_a = islands[a->index]->can_translate_(params);
|
||||
const bool can_translate_b = islands[b->index]->can_translate_(params);
|
||||
if (can_translate_a != can_translate_b) {
|
||||
return can_translate_b; /* Locked islands are placed first. */
|
||||
}
|
||||
/* TODO: Fix when (params.target_aspect_y != 1.0f) */
|
||||
|
||||
/* Choose the AABB with the longest large edge. */
|
||||
float a_u = a->uv_diagonal.x * a->aspect_y;
|
||||
float a_v = a->uv_diagonal.y;
|
||||
float b_u = b->uv_diagonal.x * b->aspect_y;
|
||||
float b_v = b->uv_diagonal.y;
|
||||
if (a_u > a_v) {
|
||||
std::swap(a_u, a_v);
|
||||
}
|
||||
if (b_u > b_v) {
|
||||
std::swap(b_u, b_v);
|
||||
}
|
||||
float diff_u = a_u - b_u;
|
||||
float diff_v = a_v - b_v;
|
||||
diff_v += diff_u * 0.05f; /* Robust sort, smooth over round-off errors. */
|
||||
if (diff_v == 0.0f) { /* Tie break. */
|
||||
return diff_u > 0.0f;
|
||||
}
|
||||
return diff_v > 0.0f;
|
||||
});
|
||||
/* Choose the AABB with the longest large edge. */
|
||||
float a_u = a->uv_diagonal.x * a->aspect_y;
|
||||
float a_v = a->uv_diagonal.y;
|
||||
float b_u = b->uv_diagonal.x * b->aspect_y;
|
||||
float b_v = b->uv_diagonal.y;
|
||||
if (a_u > a_v) {
|
||||
std::swap(a_u, a_v);
|
||||
}
|
||||
if (b_u > b_v) {
|
||||
std::swap(b_u, b_v);
|
||||
}
|
||||
float diff_u = a_u - b_u;
|
||||
float diff_v = a_v - b_v;
|
||||
diff_v += diff_u * 0.05f; /* Robust sort, smooth over round-off errors. */
|
||||
if (diff_v == 0.0f) { /* Tie break. */
|
||||
return diff_u > 0.0f;
|
||||
}
|
||||
return diff_v > 0.0f;
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::stable_sort(
|
||||
aabbs.begin(),
|
||||
aabbs.end(),
|
||||
[&](const std::unique_ptr<UVAABBIsland> &a, const std::unique_ptr<UVAABBIsland> &b) {
|
||||
const bool can_translate_a = islands[a->index]->can_translate_(params);
|
||||
const bool can_translate_b = islands[b->index]->can_translate_(params);
|
||||
if (can_translate_a != can_translate_b) {
|
||||
return can_translate_b; /* Locked islands are placed first. */
|
||||
}
|
||||
|
||||
std::stable_sort(aabbs.begin(),
|
||||
aabbs.end(),
|
||||
[¶ms, &islands](const UVAABBIsland *a, const UVAABBIsland *b) {
|
||||
const bool can_translate_a = islands[a->index]->can_translate_(params);
|
||||
const bool can_translate_b = islands[b->index]->can_translate_(params);
|
||||
if (can_translate_a != can_translate_b) {
|
||||
return can_translate_b; /* Locked islands are placed first. */
|
||||
}
|
||||
|
||||
/* Choose the AABB with larger rectangular area. */
|
||||
return b->uv_diagonal.x * b->uv_diagonal.y <
|
||||
a->uv_diagonal.x * a->uv_diagonal.y;
|
||||
});
|
||||
/* Choose the AABB with larger rectangular area. */
|
||||
return b->uv_diagonal.x * b->uv_diagonal.y < a->uv_diagonal.x * a->uv_diagonal.y;
|
||||
});
|
||||
}
|
||||
|
||||
/* If some of the islands are locked, we build a summary about them here. */
|
||||
|
@ -1499,7 +1499,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
|
||||
alpaca_cutoff = std::max(alpaca_cutoff, locked_island_count); /* ...TODO... */
|
||||
|
||||
Span<UVAABBIsland *> slow_aabbs = aabbs.as_span().take_front(
|
||||
Span<std::unique_ptr<UVAABBIsland>> slow_aabbs = aabbs.as_span().take_front(
|
||||
std::min(alpaca_cutoff, islands.size()));
|
||||
rctf extent = {0.0f, 1e30f, 0.0f, 1e30f};
|
||||
|
||||
|
@ -1546,13 +1546,6 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
r_phis,
|
||||
&final_extent);
|
||||
|
||||
/* Housekeeping. */
|
||||
for (const int64_t i : aabbs.index_range()) {
|
||||
UVAABBIsland *aabb = aabbs[i];
|
||||
aabbs[i] = nullptr;
|
||||
delete aabb;
|
||||
}
|
||||
|
||||
return get_aspect_scaled_extent(final_extent, params);
|
||||
}
|
||||
|
||||
|
|
|
@ -1070,8 +1070,8 @@ id<MTLBuffer> MTLBatch::get_emulated_toplogy_buffer(GPUPrimType &in_out_prim_typ
|
|||
case GPU_PRIM_LINE_LOOP: {
|
||||
int line = 0;
|
||||
for (line = 0; line < output_prim_count - 1; line++) {
|
||||
data[line * 3 + 0] = line + 0;
|
||||
data[line * 3 + 1] = line + 1;
|
||||
data[line * 2 + 0] = line + 0;
|
||||
data[line * 2 + 1] = line + 1;
|
||||
}
|
||||
/* Closing line. */
|
||||
data[line * 2 + 0] = line + 0;
|
||||
|
|
|
@ -1265,7 +1265,7 @@ bool MTLContext::ensure_buffer_bindings(
|
|||
const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
|
||||
|
||||
if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
|
||||
/* Explicit lookup location for SSBO in bind table.*/
|
||||
/* Explicit lookup location for SSBO in bind table. */
|
||||
const uint32_t ssbo_location = ssbo.location;
|
||||
/* buffer(N) index of where to bind the SSBO. */
|
||||
const uint32_t buffer_index = ssbo.buffer_index;
|
||||
|
@ -1444,7 +1444,7 @@ bool MTLContext::ensure_buffer_bindings(
|
|||
const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
|
||||
|
||||
if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
|
||||
/* Explicit lookup location for UBO in bind table.*/
|
||||
/* Explicit lookup location for UBO in bind table. */
|
||||
const uint32_t ssbo_location = ssbo.location;
|
||||
/* buffer(N) index of where to bind the UBO. */
|
||||
const uint32_t buffer_index = ssbo.buffer_index;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "BKE_global.h"
|
||||
#include "CLG_log.h"
|
||||
|
||||
/** Options for organising Metal GPU debug captures. */
|
||||
/** Options for organizing Metal GPU debug captures. */
|
||||
/* Maximum nested debug group depth. Groups beyond this will still have the pass name pulled into
|
||||
* the RenderCommandEncoder, but will not display in the trace.
|
||||
* Use -1 for unlimited. */
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
@ -233,17 +234,20 @@ class MTLCircularBuffer {
|
|||
struct MTLBufferHandle {
|
||||
gpu::MTLBuffer *buffer;
|
||||
uint64_t buffer_size;
|
||||
time_t insert_time;
|
||||
|
||||
inline MTLBufferHandle(gpu::MTLBuffer *buf)
|
||||
{
|
||||
this->buffer = buf;
|
||||
this->buffer_size = this->buffer->get_size();
|
||||
this->insert_time = std::time(nullptr);
|
||||
}
|
||||
|
||||
inline MTLBufferHandle(uint64_t compare_size)
|
||||
{
|
||||
this->buffer = nullptr;
|
||||
this->buffer_size = compare_size;
|
||||
this->insert_time = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -356,7 +360,6 @@ class MTLBufferPool {
|
|||
|
||||
/* Debug statistics. */
|
||||
std::atomic<int> per_frame_allocation_count_;
|
||||
std::atomic<int64_t> allocations_in_pool_;
|
||||
std::atomic<int64_t> buffers_in_pool_;
|
||||
#endif
|
||||
|
||||
|
@ -401,6 +404,7 @@ class MTLBufferPool {
|
|||
/* MTLBuffer::free() can be called from separate threads, due to usage within animation
|
||||
* system/worker threads. */
|
||||
std::atomic<MTLSafeFreeList *> current_free_list_;
|
||||
std::atomic<int64_t> allocations_in_pool_;
|
||||
|
||||
public:
|
||||
void init(id<MTLDevice> device);
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
using namespace blender;
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* Memory size in bytes macros, used as pool flushing frequency thresholds. */
|
||||
#define MEMORY_SIZE_2GB 2147483648LL
|
||||
#define MEMORY_SIZE_1GB 1073741824LL
|
||||
#define MEMORY_SIZE_512MB 536870912LL
|
||||
#define MEMORY_SIZE_256MB 268435456LL
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -28,9 +34,10 @@ void MTLBufferPool::init(id<MTLDevice> mtl_device)
|
|||
/* Debug statistics. */
|
||||
total_allocation_bytes_ = 0;
|
||||
per_frame_allocation_count_ = 0;
|
||||
allocations_in_pool_ = 0;
|
||||
buffers_in_pool_ = 0;
|
||||
#endif
|
||||
/* Track pool allocation size. */
|
||||
allocations_in_pool_ = 0;
|
||||
|
||||
/* Free pools -- Create initial safe free pool */
|
||||
BLI_assert(current_free_list_ == nullptr);
|
||||
|
@ -159,11 +166,13 @@ gpu::MTLBuffer *MTLBufferPool::allocate_aligned(uint64_t size,
|
|||
|
||||
#if MTL_DEBUG_MEMORY_STATISTICS == 1
|
||||
/* Debug. */
|
||||
allocations_in_pool_ -= new_buffer->get_size();
|
||||
buffers_in_pool_--;
|
||||
BLI_assert(allocations_in_pool_ >= 0);
|
||||
#endif
|
||||
|
||||
/* Decrement size of pool. */
|
||||
BLI_assert(allocations_in_pool_ >= 0);
|
||||
allocations_in_pool_ -= new_buffer->get_size();
|
||||
|
||||
/* Ensure buffer memory is correctly backed. */
|
||||
BLI_assert(new_buffer->get_metal_buffer());
|
||||
}
|
||||
|
@ -275,6 +284,59 @@ void MTLBufferPool::update_memory_pools()
|
|||
}
|
||||
}
|
||||
|
||||
/* Release memory allocations which have not been used in a while.
|
||||
* This ensures memory pressure stays low for scenes with compounding complexity during
|
||||
* animation.
|
||||
* If memory is continually used, then we do not want to free this memory as it will be
|
||||
* re-allocated during a short time period. */
|
||||
const time_t time_now = std::time(nullptr);
|
||||
for (auto buffer_pool_list : buffer_pools_.items()) {
|
||||
MTLBufferPoolOrderedList *pool_allocations = buffer_pool_list.value;
|
||||
MTLBufferPoolOrderedList::iterator pool_iterator = pool_allocations->begin();
|
||||
while (pool_iterator != pool_allocations->end()) {
|
||||
|
||||
const MTLBufferHandle handle = *pool_iterator;
|
||||
const time_t time_passed = time_now - handle.insert_time;
|
||||
|
||||
/* Free allocations if a certain amount of time has passed.
|
||||
* Deletion frequency depends on how much excess memory
|
||||
* the application is using. */
|
||||
time_t deletion_time_threshold_s = 600;
|
||||
/* Spare pool memory >= 2GB. */
|
||||
if (allocations_in_pool_ >= MEMORY_SIZE_2GB) {
|
||||
deletion_time_threshold_s = 2;
|
||||
}
|
||||
else
|
||||
/* Spare pool memory >= 1GB. */
|
||||
if (allocations_in_pool_ >= MEMORY_SIZE_1GB)
|
||||
{
|
||||
deletion_time_threshold_s = 4;
|
||||
}
|
||||
/* Spare pool memory >= 512MB.*/
|
||||
else if (allocations_in_pool_ >= MEMORY_SIZE_512MB) {
|
||||
deletion_time_threshold_s = 15;
|
||||
}
|
||||
/* Spare pool memory >= 256MB. */
|
||||
else if (allocations_in_pool_ >= MEMORY_SIZE_256MB) {
|
||||
deletion_time_threshold_s = 60;
|
||||
}
|
||||
|
||||
if (time_passed > deletion_time_threshold_s) {
|
||||
|
||||
/* Delete allocation. */
|
||||
delete handle.buffer;
|
||||
pool_iterator = pool_allocations->erase(pool_iterator);
|
||||
allocations_in_pool_ -= handle.buffer_size;
|
||||
#if MTL_DEBUG_MEMORY_STATISTICS == 1
|
||||
total_allocation_bytes_ -= handle.buffer_size;
|
||||
buffers_in_pool_--;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
pool_iterator++;
|
||||
}
|
||||
}
|
||||
|
||||
#if MTL_DEBUG_MEMORY_STATISTICS == 1
|
||||
printf("--- Allocation Stats ---\n");
|
||||
printf(" Num buffers processed in pool (this frame): %u\n", num_buffers_added);
|
||||
|
@ -383,10 +445,10 @@ void MTLBufferPool::insert_buffer_into_pool(MTLResourceOptions options, gpu::MTL
|
|||
|
||||
std::multiset<MTLBufferHandle, CompareMTLBuffer> *pool = buffer_pools_.lookup(options);
|
||||
pool->insert(MTLBufferHandle(buffer));
|
||||
allocations_in_pool_ += buffer->get_size();
|
||||
|
||||
#if MTL_DEBUG_MEMORY_STATISTICS == 1
|
||||
/* Debug statistics. */
|
||||
allocations_in_pool_ += buffer->get_size();
|
||||
buffers_in_pool_++;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -249,7 +249,7 @@ struct MSLTextureResource {
|
|||
MSLTextureSamplerAccess access;
|
||||
/* Whether resource is a texture sampler or an image. */
|
||||
bool is_texture_sampler;
|
||||
/* Index in shader bind table [[texture(N)]].*/
|
||||
/* Index in shader bind table `[[texture(N)]]`. */
|
||||
uint slot;
|
||||
/* Explicit bind index provided by ShaderCreateInfo. */
|
||||
uint location;
|
||||
|
|
|
@ -602,7 +602,9 @@ inline MTLTextureUsage mtl_usage_from_gpu(eGPUTextureUsage usage)
|
|||
if (usage == GPU_TEXTURE_USAGE_GENERAL) {
|
||||
return MTLTextureUsageUnknown;
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_READ) {
|
||||
/* Host read implies general read support, as the compute-based host read routine requires
|
||||
* reading of texture data. */
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_READ || usage & GPU_TEXTURE_USAGE_HOST_READ) {
|
||||
mtl_usage = mtl_usage | MTLTextureUsageShaderRead;
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_WRITE) {
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace blender::gpu {
|
|||
void VKBatch::draw_setup()
|
||||
{
|
||||
/* Currently the pipeline is rebuild on each draw command. Clearing the dirty flag for
|
||||
* consistency with the internals of GPU module. */
|
||||
* consistency with the internals of GPU module. */
|
||||
flag &= ~GPU_BATCH_DIRTY;
|
||||
|
||||
/* Finalize graphics pipeline */
|
||||
|
|
|
@ -42,7 +42,7 @@ void VKCommandBuffer::init(const VkDevice vk_device,
|
|||
|
||||
/* When a the last GHOST context is destroyed the device is deallocate. A moment later the GPU
|
||||
* context is destroyed. The first step is to activate it. Activating would retrieve the device
|
||||
* from GHOST which in that case is a VK_NULL_HANDLE.*/
|
||||
* from GHOST which in that case is a #VK_NULL_HANDLE. */
|
||||
if (vk_device == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
@ -750,4 +752,109 @@ VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test)
|
|||
return VK_CULL_MODE_NONE;
|
||||
}
|
||||
|
||||
const char *to_string(VkObjectType type)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case VK_OBJECT_TYPE_UNKNOWN:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_UNKNOWN);
|
||||
case VK_OBJECT_TYPE_INSTANCE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_INSTANCE);
|
||||
case VK_OBJECT_TYPE_PHYSICAL_DEVICE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_PHYSICAL_DEVICE);
|
||||
case VK_OBJECT_TYPE_DEVICE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEVICE);
|
||||
case VK_OBJECT_TYPE_QUEUE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_QUEUE);
|
||||
case VK_OBJECT_TYPE_SEMAPHORE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_SEMAPHORE);
|
||||
case VK_OBJECT_TYPE_COMMAND_BUFFER:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_COMMAND_BUFFER);
|
||||
case VK_OBJECT_TYPE_FENCE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_FENCE);
|
||||
case VK_OBJECT_TYPE_DEVICE_MEMORY:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEVICE_MEMORY);
|
||||
case VK_OBJECT_TYPE_BUFFER:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_BUFFER);
|
||||
case VK_OBJECT_TYPE_IMAGE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_IMAGE);
|
||||
case VK_OBJECT_TYPE_EVENT:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_EVENT);
|
||||
case VK_OBJECT_TYPE_QUERY_POOL:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_QUERY_POOL);
|
||||
case VK_OBJECT_TYPE_BUFFER_VIEW:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_BUFFER_VIEW);
|
||||
case VK_OBJECT_TYPE_IMAGE_VIEW:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_IMAGE_VIEW);
|
||||
case VK_OBJECT_TYPE_SHADER_MODULE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_SHADER_MODULE);
|
||||
case VK_OBJECT_TYPE_PIPELINE_CACHE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_PIPELINE_CACHE);
|
||||
case VK_OBJECT_TYPE_PIPELINE_LAYOUT:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_PIPELINE_LAYOUT);
|
||||
case VK_OBJECT_TYPE_RENDER_PASS:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_RENDER_PASS);
|
||||
case VK_OBJECT_TYPE_PIPELINE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_PIPELINE);
|
||||
case VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT);
|
||||
case VK_OBJECT_TYPE_SAMPLER:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_SAMPLER);
|
||||
case VK_OBJECT_TYPE_DESCRIPTOR_POOL:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_POOL);
|
||||
case VK_OBJECT_TYPE_DESCRIPTOR_SET:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_SET);
|
||||
case VK_OBJECT_TYPE_FRAMEBUFFER:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_FRAMEBUFFER);
|
||||
case VK_OBJECT_TYPE_COMMAND_POOL:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_COMMAND_POOL);
|
||||
case VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION);
|
||||
case VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE);
|
||||
case VK_OBJECT_TYPE_SURFACE_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_SURFACE_KHR);
|
||||
case VK_OBJECT_TYPE_SWAPCHAIN_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_SWAPCHAIN_KHR);
|
||||
case VK_OBJECT_TYPE_DISPLAY_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DISPLAY_KHR);
|
||||
case VK_OBJECT_TYPE_DISPLAY_MODE_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DISPLAY_MODE_KHR);
|
||||
case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT);
|
||||
#ifdef VK_ENABLE_BETA_EXTENSIONS
|
||||
case VK_OBJECT_TYPE_VIDEO_SESSION_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_VIDEO_SESSION_KHR);
|
||||
#endif
|
||||
#ifdef VK_ENABLE_BETA_EXTENSIONS
|
||||
case VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR);
|
||||
#endif
|
||||
case VK_OBJECT_TYPE_CU_MODULE_NVX:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_CU_MODULE_NVX);
|
||||
case VK_OBJECT_TYPE_CU_FUNCTION_NVX:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_CU_FUNCTION_NVX);
|
||||
case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT);
|
||||
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR);
|
||||
case VK_OBJECT_TYPE_VALIDATION_CACHE_EXT:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_VALIDATION_CACHE_EXT);
|
||||
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV);
|
||||
case VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL);
|
||||
case VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR);
|
||||
case VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV);
|
||||
case VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT);
|
||||
case VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA:
|
||||
return STRINGIFY_ARG(VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA);
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
return "NotFound";
|
||||
};
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -35,6 +35,7 @@ VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const voi
|
|||
VkIndexType to_vk_index_type(const GPUIndexBufType index_type);
|
||||
VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type);
|
||||
VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test);
|
||||
const char *to_string(VkObjectType type);
|
||||
|
||||
template<typename T> VkObjectType to_vk_object_type(T /*vk_obj*/)
|
||||
{
|
||||
|
|
|
@ -7,11 +7,15 @@
|
|||
*/
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BLI_dynstr.h"
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_debug.hh"
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.debug.vulkan"};
|
||||
|
||||
namespace blender::gpu {
|
||||
void VKContext::debug_group_begin(const char *name, int)
|
||||
{
|
||||
|
@ -70,6 +74,7 @@ void VKDebuggingTools::init(VkInstance vk_instance)
|
|||
{
|
||||
PFN_vkGetInstanceProcAddr instance_proc_addr = vkGetInstanceProcAddr;
|
||||
enabled = false;
|
||||
vk_debug_utils_messenger = nullptr;
|
||||
vkCmdBeginDebugUtilsLabelEXT_r = (PFN_vkCmdBeginDebugUtilsLabelEXT)instance_proc_addr(
|
||||
vk_instance, "vkCmdBeginDebugUtilsLabelEXT");
|
||||
vkCmdEndDebugUtilsLabelEXT_r = (PFN_vkCmdEndDebugUtilsLabelEXT)instance_proc_addr(
|
||||
|
@ -94,11 +99,15 @@ void VKDebuggingTools::init(VkInstance vk_instance)
|
|||
vk_instance, "vkSubmitDebugUtilsMessageEXT");
|
||||
if (vkCmdBeginDebugUtilsLabelEXT_r) {
|
||||
enabled = true;
|
||||
init_messenger(vk_instance);
|
||||
}
|
||||
}
|
||||
|
||||
void VKDebuggingTools::deinit()
|
||||
void VKDebuggingTools::deinit(VkInstance vk_instance)
|
||||
{
|
||||
if (enabled) {
|
||||
destroy_messenger(vk_instance);
|
||||
}
|
||||
vkCmdBeginDebugUtilsLabelEXT_r = nullptr;
|
||||
vkCmdEndDebugUtilsLabelEXT_r = nullptr;
|
||||
vkCmdInsertDebugUtilsLabelEXT_r = nullptr;
|
||||
|
@ -206,3 +215,231 @@ void pop_marker(const VKDevice &device)
|
|||
}
|
||||
|
||||
} // namespace blender::gpu::debug
|
||||
|
||||
namespace blender::gpu::debug {
|
||||
VKDebuggingTools::~VKDebuggingTools()
|
||||
{
|
||||
BLI_assert(vk_debug_utils_messenger == nullptr);
|
||||
};
|
||||
void VKDebuggingTools::print_vulkan_version()
|
||||
{
|
||||
uint32_t instanceVersion = VK_API_VERSION_1_0;
|
||||
vkEnumerateInstanceVersion(&instanceVersion);
|
||||
uint32_t major = VK_VERSION_MAJOR(instanceVersion);
|
||||
uint32_t minor = VK_VERSION_MINOR(instanceVersion);
|
||||
uint32_t patch = VK_VERSION_PATCH(instanceVersion);
|
||||
|
||||
printf("Vulkan Version:%u.%u.%u\n", major, minor, patch);
|
||||
}
|
||||
|
||||
void VKDebuggingTools::print_labels(const VkDebugUtilsMessengerCallbackDataEXT *callback_data)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (callback_data->objectCount > 0) {
|
||||
ss << std::endl;
|
||||
ss << callback_data->objectCount << " Object[s] related \n";
|
||||
for (uint32_t object = 0; object < callback_data->objectCount; ++object) {
|
||||
ss << "ObjectType[" << to_string(callback_data->pObjects[object].objectType) << "],";
|
||||
ss << "Handle[0x" << std::hex
|
||||
<< static_cast<uintptr_t>(callback_data->pObjects[object].objectHandle) << "]";
|
||||
if (callback_data->pObjects[object].pObjectName) {
|
||||
ss << ",Name[" << callback_data->pObjects[object].pObjectName << "]";
|
||||
}
|
||||
ss << std::endl;
|
||||
}
|
||||
}
|
||||
if (callback_data->cmdBufLabelCount > 0) {
|
||||
ss << std::endl;
|
||||
ss << callback_data->cmdBufLabelCount << " Command Buffer Label[s] " << std::endl;
|
||||
for (uint32_t label = 0; label < callback_data->cmdBufLabelCount; ++label) {
|
||||
if (callback_data->pCmdBufLabels[label].pLabelName) {
|
||||
ss << "CmdBufLabelName : " << callback_data->pCmdBufLabels[label].pLabelName << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callback_data->queueLabelCount > 0) {
|
||||
ss << std::endl;
|
||||
ss << callback_data->queueLabelCount << " Queue Label[s]\n";
|
||||
for (uint32_t label = 0; label < callback_data->queueLabelCount; ++label) {
|
||||
if (callback_data->pQueueLabels[label].pLabelName) {
|
||||
ss << "QueueLabelName : " << callback_data->pQueueLabels[label].pLabelName << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("%s\n", ss.str().c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL
|
||||
messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT /* message_type*/,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
|
||||
void *user_data);
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL
|
||||
messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT /* message_type*/,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
|
||||
void *user_data)
|
||||
{
|
||||
VKDebuggingTools &debugging_tools = *reinterpret_cast<VKDebuggingTools *>(user_data);
|
||||
if (debugging_tools.is_ignore(callback_data->messageIdNumber)) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
bool use_color = CLG_color_support_get(&LOG);
|
||||
UNUSED_VARS(use_color);
|
||||
bool enabled = false;
|
||||
if ((message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) ||
|
||||
(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT))
|
||||
{
|
||||
if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= CLG_SEVERITY_INFO)) {
|
||||
const char *format = "{0x%x}% s\n %s ";
|
||||
CLG_logf(LOG.type,
|
||||
CLG_SEVERITY_INFO,
|
||||
"",
|
||||
"",
|
||||
format,
|
||||
callback_data->messageIdNumber,
|
||||
callback_data->pMessageIdName,
|
||||
callback_data->pMessage);
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CLG_Severity clog_severity;
|
||||
switch (message_severity) {
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
|
||||
clog_severity = CLG_SEVERITY_WARN;
|
||||
break;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
||||
clog_severity = CLG_SEVERITY_ERROR;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
enabled = true;
|
||||
if (clog_severity == CLG_SEVERITY_ERROR) {
|
||||
const char *format = " %s {0x%x}\n %s ";
|
||||
CLG_logf(LOG.type,
|
||||
clog_severity,
|
||||
"",
|
||||
"",
|
||||
format,
|
||||
callback_data->pMessageIdName,
|
||||
callback_data->messageIdNumber,
|
||||
callback_data->pMessage);
|
||||
}
|
||||
else if (LOG.type->level >= CLG_SEVERITY_WARN) {
|
||||
const char *format = " %s {0x%x}\n %s ";
|
||||
CLG_logf(LOG.type,
|
||||
clog_severity,
|
||||
"",
|
||||
"",
|
||||
format,
|
||||
callback_data->pMessageIdName,
|
||||
callback_data->messageIdNumber,
|
||||
callback_data->pMessage);
|
||||
}
|
||||
}
|
||||
if ((enabled) && ((callback_data->objectCount > 0) || (callback_data->cmdBufLabelCount > 0) ||
|
||||
(callback_data->queueLabelCount > 0)))
|
||||
{
|
||||
debugging_tools.print_labels(callback_data);
|
||||
}
|
||||
return VK_TRUE;
|
||||
};
|
||||
|
||||
VkResult VKDebuggingTools::init_messenger(VkInstance vk_instance)
|
||||
{
|
||||
print_vulkan_version();
|
||||
vk_message_id_number_ignored.clear();
|
||||
BLI_assert(enabled);
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT create_info;
|
||||
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
create_info.messageSeverity = message_severity;
|
||||
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
create_info.pfnUserCallback = messenger_callback;
|
||||
create_info.pUserData = this;
|
||||
VkResult res = vkCreateDebugUtilsMessengerEXT_r(
|
||||
vk_instance, &create_info, nullptr, &vk_debug_utils_messenger);
|
||||
BLI_assert(res == VK_SUCCESS);
|
||||
return res;
|
||||
}
|
||||
|
||||
void VKDebuggingTools::destroy_messenger(VkInstance vk_instance)
|
||||
{
|
||||
if (vk_debug_utils_messenger == nullptr) {
|
||||
return;
|
||||
}
|
||||
BLI_assert(enabled);
|
||||
vkDestroyDebugUtilsMessengerEXT_r(vk_instance, vk_debug_utils_messenger, nullptr);
|
||||
|
||||
vk_message_id_number_ignored.clear();
|
||||
vk_debug_utils_messenger = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
bool VKDebuggingTools::is_ignore(int32_t id_number)
|
||||
{
|
||||
bool found = false;
|
||||
{
|
||||
std::scoped_lock lock(ignore_mutex);
|
||||
found = vk_message_id_number_ignored.contains(id_number);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void VKDebuggingTools::add_group(int32_t id_number)
|
||||
{
|
||||
std::scoped_lock lock(ignore_mutex);
|
||||
vk_message_id_number_ignored.add(id_number);
|
||||
};
|
||||
|
||||
void VKDebuggingTools::remove_group(int32_t id_number)
|
||||
{
|
||||
std::scoped_lock lock(ignore_mutex);
|
||||
vk_message_id_number_ignored.remove(id_number);
|
||||
};
|
||||
|
||||
void raise_message(int32_t id_number,
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT vk_severity_flag_bits,
|
||||
const char *format,
|
||||
...)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device_get();
|
||||
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
|
||||
if (debugging_tools.enabled) {
|
||||
DynStr *ds = nullptr;
|
||||
va_list arg;
|
||||
char *info = nullptr;
|
||||
|
||||
va_start(arg, format);
|
||||
|
||||
ds = BLI_dynstr_new();
|
||||
BLI_dynstr_vappendf(ds, format, arg);
|
||||
info = BLI_dynstr_get_cstring(ds);
|
||||
BLI_dynstr_free(ds);
|
||||
|
||||
va_end(arg);
|
||||
|
||||
static VkDebugUtilsMessengerCallbackDataEXT vk_call_back_data;
|
||||
vk_call_back_data.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
|
||||
vk_call_back_data.pNext = VK_NULL_HANDLE;
|
||||
vk_call_back_data.messageIdNumber = id_number;
|
||||
vk_call_back_data.pMessageIdName = "VulkanMessenger";
|
||||
vk_call_back_data.objectCount = 0;
|
||||
vk_call_back_data.flags = 0;
|
||||
vk_call_back_data.pObjects = VK_NULL_HANDLE;
|
||||
vk_call_back_data.pMessage = info;
|
||||
debugging_tools.vkSubmitDebugUtilsMessageEXT_r(device.instance_get(),
|
||||
vk_severity_flag_bits,
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT,
|
||||
&vk_call_back_data);
|
||||
MEM_freeN((void *)info);
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace blender::gpu::debug
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
#include <mutex>
|
||||
#include <typeindex>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
@ -19,8 +21,14 @@ class VKContext;
|
|||
class VKDevice;
|
||||
|
||||
namespace debug {
|
||||
struct VKDebuggingTools {
|
||||
class VKDebuggingTools {
|
||||
public:
|
||||
bool enabled = false;
|
||||
VkDebugUtilsMessageSeverityFlagsEXT message_severity =
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
/* Function pointer definitions. */
|
||||
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT_r = nullptr;
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT_r = nullptr;
|
||||
|
@ -33,9 +41,22 @@ struct VKDebuggingTools {
|
|||
PFN_vkQueueInsertDebugUtilsLabelEXT vkQueueInsertDebugUtilsLabelEXT_r = nullptr;
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT_r = nullptr;
|
||||
PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT_r = nullptr;
|
||||
|
||||
VKDebuggingTools() = default;
|
||||
~VKDebuggingTools();
|
||||
void init(VkInstance vk_instance);
|
||||
void deinit();
|
||||
void deinit(VkInstance vk_instance);
|
||||
bool is_ignore(int32_t id_number);
|
||||
VkResult init_messenger(VkInstance vk_instance);
|
||||
void destroy_messenger(VkInstance vk_instance);
|
||||
void print_labels(const VkDebugUtilsMessengerCallbackDataEXT *callback_data);
|
||||
|
||||
private:
|
||||
VkDebugUtilsMessengerEXT vk_debug_utils_messenger = nullptr;
|
||||
Set<int32_t> vk_message_id_number_ignored;
|
||||
std::mutex ignore_mutex;
|
||||
void print_vulkan_version();
|
||||
void add_group(int32_t id_number);
|
||||
void remove_group(int32_t id_number);
|
||||
};
|
||||
|
||||
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name);
|
||||
|
@ -58,5 +79,11 @@ void pop_marker(VkCommandBuffer vk_command_buffer);
|
|||
void push_marker(const VKDevice &device, const char *name);
|
||||
void set_marker(const VKDevice &device, const char *name);
|
||||
void pop_marker(const VKDevice &device);
|
||||
/* how to use : debug::raise_message(0xB41ca2,VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,"This
|
||||
* is a raise message. %llx", (uintptr_t)vk_object); */
|
||||
void raise_message(int32_t id_number,
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT vk_severity_flag_bits,
|
||||
const char *fmt,
|
||||
...);
|
||||
} // namespace debug
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -18,7 +18,7 @@ void VKDevice::deinit()
|
|||
{
|
||||
vmaDestroyAllocator(mem_allocator_);
|
||||
mem_allocator_ = VK_NULL_HANDLE;
|
||||
debugging_tools_.deinit();
|
||||
debugging_tools_.deinit(vk_instance_);
|
||||
|
||||
vk_instance_ = VK_NULL_HANDLE;
|
||||
vk_physical_device_ = VK_NULL_HANDLE;
|
||||
|
|
|
@ -441,7 +441,7 @@ void VKFrameBuffer::render_pass_create()
|
|||
size_set(size[0], size[1]);
|
||||
}
|
||||
else {
|
||||
/* A framebuffer should at least be 1 by 1.*/
|
||||
/* A frame-buffer should at least be 1 by 1. */
|
||||
this->size_set(1, 1);
|
||||
}
|
||||
viewport_reset();
|
||||
|
|
|
@ -113,9 +113,9 @@ class VKFrameBuffer : public FrameBuffer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Is this framebuffer immutable?
|
||||
* Is this frame-buffer immutable?
|
||||
*
|
||||
* Framebuffers that are owned by GHOST are immutable and
|
||||
* Frame-buffers that are owned by GHOST are immutable and
|
||||
* don't have any attachments assigned. It should be assumed that there is a single color texture
|
||||
* in slot 0.
|
||||
*/
|
||||
|
|
|
@ -79,9 +79,8 @@ void VKPipelineStateManager::finalize_color_blend_state(const VKFrameBuffer &fra
|
|||
{
|
||||
color_blend_attachments.clear();
|
||||
if (framebuffer.is_immutable()) {
|
||||
/* Immutable framebuffers are owned by GHOST and don't have any attachments assigned. In this
|
||||
* case we assume that there is a single color texture assigned.
|
||||
*/
|
||||
/* Immutable frame-buffers are owned by GHOST and don't have any attachments assigned. In this
|
||||
* case we assume that there is a single color texture assigned. */
|
||||
color_blend_attachments.append(color_blend_attachment_template);
|
||||
}
|
||||
else {
|
||||
|
@ -96,7 +95,7 @@ void VKPipelineStateManager::finalize_color_blend_state(const VKFrameBuffer &fra
|
|||
else {
|
||||
/* Test to detect if all color textures are sequential attached from the first slot. We
|
||||
* assume at this moment that this is the case. Otherwise we need to rewire how attachments
|
||||
* and bindings work.*/
|
||||
* and bindings work. */
|
||||
is_sequential = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class VKUniformBuffer;
|
|||
class VKVertexBuffer;
|
||||
|
||||
class VKStateManager : public StateManager {
|
||||
/* Dummy sampler for now.*/
|
||||
/* Dummy sampler for now. */
|
||||
VKSampler sampler_;
|
||||
|
||||
uint texture_unpack_row_length_ = 0;
|
||||
|
@ -48,7 +48,7 @@ class VKStateManager : public StateManager {
|
|||
|
||||
void issue_barrier(eGPUBarrier barrier_bits) override;
|
||||
|
||||
/** Apply resources to the bindings of the active shader.*/
|
||||
/** Apply resources to the bindings of the active shader. */
|
||||
void apply_bindings();
|
||||
|
||||
void texture_bind(Texture *tex, GPUSamplerState sampler, int unit) override;
|
||||
|
|
|
@ -55,7 +55,7 @@ class VKVertexBuffer : public VertBuf {
|
|||
void allocate();
|
||||
void *convert() const;
|
||||
|
||||
/* VKTexture requires access to `buffer_` to convert a vertex buffer to a texture.*/
|
||||
/* VKTexture requires access to `buffer_` to convert a vertex buffer to a texture. */
|
||||
friend class VKTexture;
|
||||
};
|
||||
|
||||
|
|
|
@ -119,10 +119,12 @@ static bool export_params_valid(const USDExportParams ¶ms)
|
|||
return valid;
|
||||
}
|
||||
|
||||
/* Create the root Xform primitive, if the Root Prim path has been set
|
||||
/**
|
||||
* Create the root Xform primitive, if the Root Prim path has been set
|
||||
* in the export options. In the future, this function can be extended
|
||||
* to author transforms and additional schema data (e.g., model Kind)
|
||||
* on the root prim. */
|
||||
* on the root prim.
|
||||
*/
|
||||
static void ensure_root_prim(pxr::UsdStageRefPtr stage, const USDExportParams ¶ms)
|
||||
{
|
||||
if (params.root_prim_path[0] == '\0') {
|
||||
|
|
|
@ -34,7 +34,7 @@ struct NodePlacementContext {
|
|||
/* Map a USD shader prim path to the Blender node converted
|
||||
* from that shader. This map is updated during shader
|
||||
* conversion and is used to avoid creating duplicate nodes
|
||||
* for a given shader. */
|
||||
* for a given shader. */
|
||||
ShaderToNodeMap node_cache;
|
||||
|
||||
NodePlacementContext(float in_origx,
|
||||
|
|
|
@ -275,7 +275,7 @@ static void populate_curve_props_for_nurbs(const bke::CurvesGeometry &geometry,
|
|||
const bool is_cyclic)
|
||||
{
|
||||
/* Order and range, when representing a batched NurbsCurve should be authored one value per
|
||||
* curve.*/
|
||||
* curve. */
|
||||
const int num_curves = geometry.curve_num;
|
||||
orders.resize(num_curves);
|
||||
|
||||
|
|
|
@ -711,6 +711,15 @@ enum {
|
|||
* was kept around (because e.g. detected as user-edited).
|
||||
*/
|
||||
LIB_LIB_OVERRIDE_RESYNC_LEFTOVER = 1 << 13,
|
||||
/**
|
||||
* This `id` was explicitly copied as part of a clipboard copy operation.
|
||||
* When reading the clipboard back, this can be used to check which ID's are
|
||||
* intended to be part of the clipboard, compared with ID's that were indirectly referenced.
|
||||
*
|
||||
* While the flag is typically cleared, a saved file may have this set for some data-blocks,
|
||||
* so it must be treated as dirty.
|
||||
*/
|
||||
LIB_CLIPBOARD_MARK = 1 << 14,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -201,7 +201,6 @@ typedef enum uiPanelDataExpansion {
|
|||
UI_SUBPANEL_DATA_EXPAND_13 = (1 << 13),
|
||||
UI_SUBPANEL_DATA_EXPAND_14 = (1 << 14),
|
||||
UI_SUBPANEL_DATA_EXPAND_15 = (1 << 15),
|
||||
UI_SUBPANEL_DATA_EXPAND_16 = (1 << 16),
|
||||
} uiPanelDataExpansion;
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,207 +5,18 @@
|
|||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_instances.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_pointcloud.h"
|
||||
|
||||
#include "GEO_mesh_copy_selection.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_delete_geometry_cc {
|
||||
|
||||
template<typename T>
|
||||
static void copy_data_based_on_map(const Span<T> src,
|
||||
const Span<int> index_map,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
threading::parallel_for(index_map.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i_src : range) {
|
||||
const int i_dst = index_map[i_src];
|
||||
if (i_dst != -1) {
|
||||
dst[i_dst] = src[i_src];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void copy_attributes_based_on_map(const bke::AttributeAccessor src_attributes,
|
||||
bke::MutableAttributeAccessor dst_attributes,
|
||||
const eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip,
|
||||
const Span<int> index_map)
|
||||
{
|
||||
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (meta_data.domain != domain) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
if (skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
const GVArraySpan src = *src_attributes.lookup(id);
|
||||
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
|
||||
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
copy_data_based_on_map(src.typed<T>(), index_map, dst.span.typed<T>());
|
||||
});
|
||||
dst.finish();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void copy_face_corner_attributes(const bke::AttributeAccessor src_attributes,
|
||||
bke::MutableAttributeAccessor dst_attributes,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip,
|
||||
const int selected_loops_num,
|
||||
const Span<int> selected_poly_indices,
|
||||
const Mesh &mesh_in)
|
||||
{
|
||||
const OffsetIndices polys = mesh_in.polys();
|
||||
Vector<int64_t> indices;
|
||||
indices.reserve(selected_loops_num);
|
||||
for (const int src_poly_index : selected_poly_indices) {
|
||||
for (const int corner : polys[src_poly_index]) {
|
||||
indices.append_unchecked(corner);
|
||||
}
|
||||
}
|
||||
IndexMaskMemory memory;
|
||||
bke::gather_attributes(src_attributes,
|
||||
ATTR_DOMAIN_CORNER,
|
||||
propagation_info,
|
||||
skip,
|
||||
IndexMask::from_indices<int64_t>(indices, memory),
|
||||
dst_attributes);
|
||||
}
|
||||
|
||||
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map)
|
||||
{
|
||||
BLI_assert(src_mesh.totedge == edge_map.size());
|
||||
const Span<int2> src_edges = src_mesh.edges();
|
||||
MutableSpan<int2> dst_edges = dst_mesh.edges_for_write();
|
||||
|
||||
threading::parallel_for(src_edges.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i_src : range) {
|
||||
const int i_dst = edge_map[i_src];
|
||||
if (ELEM(i_dst, -1, -2)) {
|
||||
continue;
|
||||
}
|
||||
dst_edges[i_dst] = src_edges[i_src];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
|
||||
Mesh &dst_mesh,
|
||||
Span<int> vertex_map,
|
||||
Span<int> edge_map)
|
||||
{
|
||||
BLI_assert(src_mesh.totvert == vertex_map.size());
|
||||
BLI_assert(src_mesh.totedge == edge_map.size());
|
||||
const Span<int2> src_edges = src_mesh.edges();
|
||||
MutableSpan<int2> dst_edges = dst_mesh.edges_for_write();
|
||||
|
||||
threading::parallel_for(src_edges.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i_src : range) {
|
||||
const int i_dst = edge_map[i_src];
|
||||
if (i_dst == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dst_edges[i_dst][0] = vertex_map[src_edges[i_src][0]];
|
||||
dst_edges[i_dst][1] = vertex_map[src_edges[i_src][1]];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Faces and edges changed but vertices are the same. */
|
||||
static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
|
||||
Mesh &dst_mesh,
|
||||
Span<int> edge_map,
|
||||
Span<int> masked_poly_indices,
|
||||
Span<int> new_loop_starts)
|
||||
{
|
||||
const OffsetIndices src_polys = src_mesh.polys();
|
||||
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
||||
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
||||
MutableSpan<int> dst_poly_offsets = dst_mesh.poly_offsets_for_write();
|
||||
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
|
||||
MutableSpan<int> dst_corner_edges = dst_mesh.corner_edges_for_write();
|
||||
|
||||
threading::parallel_for(masked_poly_indices.index_range(), 512, [&](const IndexRange range) {
|
||||
for (const int i_dst : range) {
|
||||
const int i_src = masked_poly_indices[i_dst];
|
||||
const IndexRange poly_src = src_polys[i_src];
|
||||
const Span<int> src_poly_verts = src_corner_verts.slice(poly_src);
|
||||
const Span<int> src_poly_edges = src_corner_edges.slice(poly_src);
|
||||
|
||||
dst_poly_offsets[i_dst] = new_loop_starts[i_dst];
|
||||
MutableSpan<int> dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst],
|
||||
poly_src.size());
|
||||
MutableSpan<int> dst_poly_edges = dst_corner_edges.slice(dst_poly_offsets[i_dst],
|
||||
poly_src.size());
|
||||
|
||||
dst_poly_verts.copy_from(src_poly_verts);
|
||||
|
||||
for (const int i : IndexRange(poly_src.size())) {
|
||||
dst_poly_edges[i] = edge_map[src_poly_edges[i]];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
|
||||
Mesh &dst_mesh,
|
||||
Span<int> vertex_map,
|
||||
Span<int> edge_map,
|
||||
Span<int> masked_poly_indices,
|
||||
Span<int> new_loop_starts)
|
||||
{
|
||||
const OffsetIndices src_polys = src_mesh.polys();
|
||||
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
||||
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
||||
MutableSpan<int> dst_poly_offsets = dst_mesh.poly_offsets_for_write();
|
||||
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
|
||||
MutableSpan<int> dst_corner_edges = dst_mesh.corner_edges_for_write();
|
||||
|
||||
threading::parallel_for(masked_poly_indices.index_range(), 512, [&](const IndexRange range) {
|
||||
for (const int i_dst : range) {
|
||||
const int i_src = masked_poly_indices[i_dst];
|
||||
const IndexRange poly_src = src_polys[i_src];
|
||||
const Span<int> src_poly_verts = src_corner_verts.slice(poly_src);
|
||||
const Span<int> src_poly_edges = src_corner_edges.slice(poly_src);
|
||||
|
||||
dst_poly_offsets[i_dst] = new_loop_starts[i_dst];
|
||||
MutableSpan<int> dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst],
|
||||
poly_src.size());
|
||||
MutableSpan<int> dst_poly_edges = dst_corner_edges.slice(dst_poly_offsets[i_dst],
|
||||
poly_src.size());
|
||||
|
||||
for (const int i : IndexRange(poly_src.size())) {
|
||||
dst_poly_verts[i] = vertex_map[src_poly_verts[i]];
|
||||
}
|
||||
for (const int i : IndexRange(poly_src.size())) {
|
||||
dst_poly_edges[i] = edge_map[src_poly_edges[i]];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** \return std::nullopt if the geometry should remain unchanged. */
|
||||
static std::optional<Curves *> separate_curves_selection(
|
||||
const Curves &src_curves_id,
|
||||
|
@ -291,729 +102,24 @@ static void delete_selected_instances(GeometrySet &geometry_set,
|
|||
instances.remove(selection, propagation_info);
|
||||
}
|
||||
|
||||
static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection,
|
||||
MutableSpan<int> r_vertex_map,
|
||||
int *r_selected_verts_num)
|
||||
{
|
||||
BLI_assert(vertex_selection.size() == r_vertex_map.size());
|
||||
|
||||
int selected_verts_num = 0;
|
||||
for (const int i : r_vertex_map.index_range()) {
|
||||
if (vertex_selection[i]) {
|
||||
r_vertex_map[i] = selected_verts_num;
|
||||
selected_verts_num++;
|
||||
}
|
||||
else {
|
||||
r_vertex_map[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
*r_selected_verts_num = selected_verts_num;
|
||||
}
|
||||
|
||||
static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
|
||||
const Span<bool> vertex_selection,
|
||||
MutableSpan<int> r_edge_map,
|
||||
int *r_selected_edges_num)
|
||||
{
|
||||
BLI_assert(mesh.totedge == r_edge_map.size());
|
||||
const Span<int2> edges = mesh.edges();
|
||||
|
||||
int selected_edges_num = 0;
|
||||
for (const int i : IndexRange(mesh.totedge)) {
|
||||
const int2 &edge = edges[i];
|
||||
|
||||
/* Only add the edge if both vertices will be in the new mesh. */
|
||||
if (vertex_selection[edge[0]] && vertex_selection[edge[1]]) {
|
||||
r_edge_map[i] = selected_edges_num;
|
||||
selected_edges_num++;
|
||||
}
|
||||
else {
|
||||
r_edge_map[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
*r_selected_edges_num = selected_edges_num;
|
||||
}
|
||||
|
||||
static void compute_selected_polys_from_vertex_selection(const Mesh &mesh,
|
||||
const Span<bool> vertex_selection,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
BLI_assert(mesh.totvert == vertex_selection.size());
|
||||
const OffsetIndices polys = mesh.polys();
|
||||
const Span<int> corner_verts = mesh.corner_verts();
|
||||
|
||||
r_selected_poly_indices.reserve(mesh.totpoly);
|
||||
r_loop_starts.reserve(mesh.totloop);
|
||||
|
||||
int selected_loops_num = 0;
|
||||
for (const int i : polys.index_range()) {
|
||||
const IndexRange poly_src = polys[i];
|
||||
|
||||
bool all_verts_in_selection = true;
|
||||
for (const int vert : corner_verts.slice(poly_src)) {
|
||||
if (!vertex_selection[vert]) {
|
||||
all_verts_in_selection = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_verts_in_selection) {
|
||||
r_selected_poly_indices.append_unchecked(i);
|
||||
r_loop_starts.append_unchecked(selected_loops_num);
|
||||
selected_loops_num += poly_src.size();
|
||||
}
|
||||
}
|
||||
|
||||
*r_selected_polys_num = r_selected_poly_indices.size();
|
||||
*r_selected_loops_num = selected_loops_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the
|
||||
* edge are kept along with the edge.
|
||||
*/
|
||||
static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh,
|
||||
const Span<bool> edge_selection,
|
||||
MutableSpan<int> r_vertex_map,
|
||||
MutableSpan<int> r_edge_map,
|
||||
int *r_selected_verts_num,
|
||||
int *r_selected_edges_num)
|
||||
{
|
||||
BLI_assert(mesh.totedge == edge_selection.size());
|
||||
const Span<int2> edges = mesh.edges();
|
||||
|
||||
int selected_edges_num = 0;
|
||||
int selected_verts_num = 0;
|
||||
for (const int i : IndexRange(mesh.totedge)) {
|
||||
const int2 &edge = edges[i];
|
||||
if (edge_selection[i]) {
|
||||
r_edge_map[i] = selected_edges_num;
|
||||
selected_edges_num++;
|
||||
if (r_vertex_map[edge[0]] == -1) {
|
||||
r_vertex_map[edge[0]] = selected_verts_num;
|
||||
selected_verts_num++;
|
||||
}
|
||||
if (r_vertex_map[edge[1]] == -1) {
|
||||
r_vertex_map[edge[1]] = selected_verts_num;
|
||||
selected_verts_num++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
r_edge_map[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
*r_selected_verts_num = selected_verts_num;
|
||||
*r_selected_edges_num = selected_edges_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every edge if it is in `edge_selection`.
|
||||
*/
|
||||
static void compute_selected_edges_from_edge_selection(const Mesh &mesh,
|
||||
const Span<bool> edge_selection,
|
||||
MutableSpan<int> r_edge_map,
|
||||
int *r_selected_edges_num)
|
||||
{
|
||||
BLI_assert(mesh.totedge == edge_selection.size());
|
||||
|
||||
int selected_edges_num = 0;
|
||||
for (const int i : IndexRange(mesh.totedge)) {
|
||||
if (edge_selection[i]) {
|
||||
r_edge_map[i] = selected_edges_num;
|
||||
selected_edges_num++;
|
||||
}
|
||||
else {
|
||||
r_edge_map[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
*r_selected_edges_num = selected_edges_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every polygon if all the edges are in `edge_selection`. If they are, then that
|
||||
* polygon is kept.
|
||||
*/
|
||||
static void compute_selected_polys_from_edge_selection(const Mesh &mesh,
|
||||
const Span<bool> edge_selection,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
const OffsetIndices polys = mesh.polys();
|
||||
const Span<int> corner_edges = mesh.corner_edges();
|
||||
|
||||
r_selected_poly_indices.reserve(mesh.totpoly);
|
||||
r_loop_starts.reserve(mesh.totloop);
|
||||
|
||||
int selected_loops_num = 0;
|
||||
for (const int i : polys.index_range()) {
|
||||
const IndexRange poly_src = polys[i];
|
||||
|
||||
bool all_edges_in_selection = true;
|
||||
for (const int edge : corner_edges.slice(poly_src)) {
|
||||
if (!edge_selection[edge]) {
|
||||
all_edges_in_selection = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_edges_in_selection) {
|
||||
r_selected_poly_indices.append_unchecked(i);
|
||||
r_loop_starts.append_unchecked(selected_loops_num);
|
||||
selected_loops_num += poly_src.size();
|
||||
}
|
||||
}
|
||||
|
||||
*r_selected_polys_num = r_selected_poly_indices.size();
|
||||
*r_selected_loops_num = selected_loops_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every edge and polygon if all its vertices are in `vertex_selection`.
|
||||
*/
|
||||
static void compute_selected_mesh_data_from_vertex_selection_edge_face(
|
||||
static std::optional<Mesh *> separate_mesh_selection(
|
||||
const Mesh &mesh,
|
||||
const Span<bool> vertex_selection,
|
||||
MutableSpan<int> r_edge_map,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_edges_num,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
const Field<bool> &selection,
|
||||
const eAttrDomain selection_domain,
|
||||
const GeometryNodeDeleteGeometryMode mode,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
threading::parallel_invoke(
|
||||
mesh.totedge > 1000,
|
||||
[&]() {
|
||||
compute_selected_edges_from_vertex_selection(
|
||||
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
|
||||
},
|
||||
[&]() {
|
||||
compute_selected_polys_from_vertex_selection(mesh,
|
||||
vertex_selection,
|
||||
r_selected_poly_indices,
|
||||
r_loop_starts,
|
||||
r_selected_polys_num,
|
||||
r_selected_loops_num);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all
|
||||
* vertices of that polygon or edge are in the selection.
|
||||
*/
|
||||
static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
|
||||
const Span<bool> vertex_selection,
|
||||
MutableSpan<int> r_vertex_map,
|
||||
MutableSpan<int> r_edge_map,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_verts_num,
|
||||
int *r_selected_edges_num,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
threading::parallel_invoke(
|
||||
mesh.totedge > 1000,
|
||||
[&]() {
|
||||
compute_selected_verts_from_vertex_selection(
|
||||
vertex_selection, r_vertex_map, r_selected_verts_num);
|
||||
},
|
||||
[&]() {
|
||||
compute_selected_edges_from_vertex_selection(
|
||||
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
|
||||
},
|
||||
[&]() {
|
||||
compute_selected_polys_from_vertex_selection(mesh,
|
||||
vertex_selection,
|
||||
r_selected_poly_indices,
|
||||
r_loop_starts,
|
||||
r_selected_polys_num,
|
||||
r_selected_loops_num);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in
|
||||
* the selection.
|
||||
*/
|
||||
static void compute_selected_mesh_data_from_edge_selection_edge_face(
|
||||
const Mesh &mesh,
|
||||
const Span<bool> edge_selection,
|
||||
MutableSpan<int> r_edge_map,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_edges_num,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
threading::parallel_invoke(
|
||||
mesh.totedge > 1000,
|
||||
[&]() {
|
||||
compute_selected_edges_from_edge_selection(
|
||||
mesh, edge_selection, r_edge_map, r_selected_edges_num);
|
||||
},
|
||||
[&]() {
|
||||
compute_selected_polys_from_edge_selection(mesh,
|
||||
edge_selection,
|
||||
r_selected_poly_indices,
|
||||
r_loop_starts,
|
||||
r_selected_polys_num,
|
||||
r_selected_loops_num);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to
|
||||
* that edge are kept as well. The polys are kept if all edges are in the selection.
|
||||
*/
|
||||
static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
|
||||
const Span<bool> edge_selection,
|
||||
MutableSpan<int> r_vertex_map,
|
||||
MutableSpan<int> r_edge_map,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_verts_num,
|
||||
int *r_selected_edges_num,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
threading::parallel_invoke(
|
||||
mesh.totedge > 1000,
|
||||
[&]() {
|
||||
r_vertex_map.fill(-1);
|
||||
compute_selected_verts_and_edges_from_edge_selection(mesh,
|
||||
edge_selection,
|
||||
r_vertex_map,
|
||||
r_edge_map,
|
||||
r_selected_verts_num,
|
||||
r_selected_edges_num);
|
||||
},
|
||||
[&]() {
|
||||
compute_selected_polys_from_edge_selection(mesh,
|
||||
edge_selection,
|
||||
r_selected_poly_indices,
|
||||
r_loop_starts,
|
||||
r_selected_polys_num,
|
||||
r_selected_loops_num);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every polygon if it is in `poly_selection`.
|
||||
*/
|
||||
static void compute_selected_polys_from_poly_selection(const Mesh &mesh,
|
||||
const Span<bool> poly_selection,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
BLI_assert(mesh.totpoly == poly_selection.size());
|
||||
const OffsetIndices polys = mesh.polys();
|
||||
|
||||
r_selected_poly_indices.reserve(mesh.totpoly);
|
||||
r_loop_starts.reserve(mesh.totloop);
|
||||
|
||||
int selected_loops_num = 0;
|
||||
for (const int i : polys.index_range()) {
|
||||
const IndexRange poly_src = polys[i];
|
||||
/* We keep this one. */
|
||||
if (poly_selection[i]) {
|
||||
r_selected_poly_indices.append_unchecked(i);
|
||||
r_loop_starts.append_unchecked(selected_loops_num);
|
||||
selected_loops_num += poly_src.size();
|
||||
}
|
||||
}
|
||||
*r_selected_polys_num = r_selected_poly_indices.size();
|
||||
*r_selected_loops_num = selected_loops_num;
|
||||
}
|
||||
/**
|
||||
* Checks for every polygon if it is in `poly_selection`. If it is, the edges
|
||||
* belonging to that polygon are kept as well.
|
||||
*/
|
||||
static void compute_selected_mesh_data_from_poly_selection_edge_face(
|
||||
const Mesh &mesh,
|
||||
const Span<bool> poly_selection,
|
||||
MutableSpan<int> r_edge_map,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_edges_num,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
BLI_assert(mesh.totpoly == poly_selection.size());
|
||||
BLI_assert(mesh.totedge == r_edge_map.size());
|
||||
const OffsetIndices polys = mesh.polys();
|
||||
const Span<int> corner_edges = mesh.corner_edges();
|
||||
|
||||
r_edge_map.fill(-1);
|
||||
|
||||
r_selected_poly_indices.reserve(mesh.totpoly);
|
||||
r_loop_starts.reserve(mesh.totloop);
|
||||
|
||||
int selected_loops_num = 0;
|
||||
int selected_edges_num = 0;
|
||||
for (const int i : polys.index_range()) {
|
||||
const IndexRange poly_src = polys[i];
|
||||
/* We keep this one. */
|
||||
if (poly_selection[i]) {
|
||||
r_selected_poly_indices.append_unchecked(i);
|
||||
r_loop_starts.append_unchecked(selected_loops_num);
|
||||
selected_loops_num += poly_src.size();
|
||||
|
||||
/* Add the vertices and the edges. */
|
||||
for (const int edge : corner_edges.slice(poly_src)) {
|
||||
/* Check first if it has not yet been added. */
|
||||
if (r_edge_map[edge] == -1) {
|
||||
r_edge_map[edge] = selected_edges_num;
|
||||
selected_edges_num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*r_selected_edges_num = selected_edges_num;
|
||||
*r_selected_polys_num = r_selected_poly_indices.size();
|
||||
*r_selected_loops_num = selected_loops_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices
|
||||
* belonging to that polygon are kept as well.
|
||||
*/
|
||||
static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
|
||||
const Span<bool> poly_selection,
|
||||
MutableSpan<int> r_vertex_map,
|
||||
MutableSpan<int> r_edge_map,
|
||||
Vector<int> &r_selected_poly_indices,
|
||||
Vector<int> &r_loop_starts,
|
||||
int *r_selected_verts_num,
|
||||
int *r_selected_edges_num,
|
||||
int *r_selected_polys_num,
|
||||
int *r_selected_loops_num)
|
||||
{
|
||||
BLI_assert(mesh.totpoly == poly_selection.size());
|
||||
BLI_assert(mesh.totedge == r_edge_map.size());
|
||||
const OffsetIndices polys = mesh.polys();
|
||||
const Span<int> corner_verts = mesh.corner_verts();
|
||||
const Span<int> corner_edges = mesh.corner_edges();
|
||||
|
||||
r_vertex_map.fill(-1);
|
||||
r_edge_map.fill(-1);
|
||||
|
||||
r_selected_poly_indices.reserve(mesh.totpoly);
|
||||
r_loop_starts.reserve(mesh.totloop);
|
||||
|
||||
int selected_loops_num = 0;
|
||||
int selected_verts_num = 0;
|
||||
int selected_edges_num = 0;
|
||||
for (const int i : polys.index_range()) {
|
||||
const IndexRange poly_src = polys[i];
|
||||
/* We keep this one. */
|
||||
if (poly_selection[i]) {
|
||||
r_selected_poly_indices.append_unchecked(i);
|
||||
r_loop_starts.append_unchecked(selected_loops_num);
|
||||
selected_loops_num += poly_src.size();
|
||||
|
||||
/* Add the vertices and the edges. */
|
||||
for (const int corner : poly_src) {
|
||||
const int vert = corner_verts[corner];
|
||||
const int edge = corner_edges[corner];
|
||||
/* Check first if it has not yet been added. */
|
||||
if (r_vertex_map[vert] == -1) {
|
||||
r_vertex_map[vert] = selected_verts_num;
|
||||
selected_verts_num++;
|
||||
}
|
||||
if (r_edge_map[edge] == -1) {
|
||||
r_edge_map[edge] = selected_edges_num;
|
||||
selected_edges_num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*r_selected_verts_num = selected_verts_num;
|
||||
*r_selected_edges_num = selected_edges_num;
|
||||
*r_selected_polys_num = r_selected_poly_indices.size();
|
||||
*r_selected_loops_num = selected_loops_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the parts of the mesh that are in the selection.
|
||||
*/
|
||||
static void do_mesh_separation(GeometrySet &geometry_set,
|
||||
const Mesh &mesh_in,
|
||||
const Span<bool> selection,
|
||||
const eAttrDomain domain,
|
||||
const GeometryNodeDeleteGeometryMode mode,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
/* Needed in all cases. */
|
||||
Vector<int> selected_poly_indices;
|
||||
Vector<int> new_loop_starts;
|
||||
int selected_polys_num = 0;
|
||||
int selected_loops_num = 0;
|
||||
|
||||
IndexMaskMemory memory;
|
||||
|
||||
Mesh *mesh_out;
|
||||
|
||||
switch (mode) {
|
||||
case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: {
|
||||
Array<int> vertex_map(mesh_in.totvert);
|
||||
int selected_verts_num = 0;
|
||||
|
||||
Array<int> edge_map(mesh_in.totedge);
|
||||
int selected_edges_num = 0;
|
||||
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
compute_selected_mesh_data_from_vertex_selection(mesh_in,
|
||||
selection,
|
||||
vertex_map,
|
||||
edge_map,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_verts_num,
|
||||
&selected_edges_num,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
compute_selected_mesh_data_from_edge_selection(mesh_in,
|
||||
selection,
|
||||
vertex_map,
|
||||
edge_map,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_verts_num,
|
||||
&selected_edges_num,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
compute_selected_mesh_data_from_poly_selection(mesh_in,
|
||||
selection,
|
||||
vertex_map,
|
||||
edge_map,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_verts_num,
|
||||
&selected_edges_num,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in,
|
||||
selected_verts_num,
|
||||
selected_edges_num,
|
||||
selected_polys_num,
|
||||
selected_loops_num);
|
||||
|
||||
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map);
|
||||
copy_masked_polys_to_new_mesh(
|
||||
mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
|
||||
|
||||
copy_attributes_based_on_map(mesh_in.attributes(),
|
||||
mesh_out->attributes_for_write(),
|
||||
ATTR_DOMAIN_POINT,
|
||||
propagation_info,
|
||||
{},
|
||||
vertex_map);
|
||||
copy_attributes_based_on_map(mesh_in.attributes(),
|
||||
mesh_out->attributes_for_write(),
|
||||
ATTR_DOMAIN_EDGE,
|
||||
propagation_info,
|
||||
{".edge_verts"},
|
||||
edge_map);
|
||||
bke::gather_attributes(mesh_in.attributes(),
|
||||
ATTR_DOMAIN_FACE,
|
||||
propagation_info,
|
||||
{},
|
||||
IndexMask::from_indices(selected_poly_indices.as_span(), memory),
|
||||
mesh_out->attributes_for_write());
|
||||
copy_face_corner_attributes(mesh_in.attributes(),
|
||||
mesh_out->attributes_for_write(),
|
||||
propagation_info,
|
||||
{".corner_vert", ".corner_edge"},
|
||||
selected_loops_num,
|
||||
selected_poly_indices,
|
||||
mesh_in);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: {
|
||||
Array<int> edge_map(mesh_in.totedge);
|
||||
int selected_edges_num = 0;
|
||||
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in,
|
||||
selection,
|
||||
edge_map,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_edges_num,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in,
|
||||
selection,
|
||||
edge_map,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_edges_num,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in,
|
||||
selection,
|
||||
edge_map,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_edges_num,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
mesh_out = BKE_mesh_new_nomain_from_template(
|
||||
&mesh_in, mesh_in.totvert, selected_edges_num, selected_polys_num, selected_loops_num);
|
||||
|
||||
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map);
|
||||
copy_masked_polys_to_new_mesh(
|
||||
mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts);
|
||||
|
||||
bke::copy_attributes(mesh_in.attributes(),
|
||||
ATTR_DOMAIN_POINT,
|
||||
propagation_info,
|
||||
{},
|
||||
mesh_out->attributes_for_write());
|
||||
copy_attributes_based_on_map(mesh_in.attributes(),
|
||||
mesh_out->attributes_for_write(),
|
||||
ATTR_DOMAIN_EDGE,
|
||||
propagation_info,
|
||||
{".edge_verts"},
|
||||
edge_map);
|
||||
bke::gather_attributes(mesh_in.attributes(),
|
||||
ATTR_DOMAIN_FACE,
|
||||
propagation_info,
|
||||
{},
|
||||
IndexMask::from_indices(selected_poly_indices.as_span(), memory),
|
||||
mesh_out->attributes_for_write());
|
||||
copy_face_corner_attributes(mesh_in.attributes(),
|
||||
mesh_out->attributes_for_write(),
|
||||
propagation_info,
|
||||
{".corner_vert", ".corner_edge"},
|
||||
selected_loops_num,
|
||||
selected_poly_indices,
|
||||
mesh_in);
|
||||
|
||||
/* Positions are not changed by the operation, so the bounds are the same. */
|
||||
mesh_out->runtime->bounds_cache = mesh_in.runtime->bounds_cache;
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: {
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
compute_selected_polys_from_vertex_selection(mesh_in,
|
||||
selection,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
compute_selected_polys_from_edge_selection(mesh_in,
|
||||
selection,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
compute_selected_polys_from_poly_selection(mesh_in,
|
||||
selection,
|
||||
selected_poly_indices,
|
||||
new_loop_starts,
|
||||
&selected_polys_num,
|
||||
&selected_loops_num);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
mesh_out = BKE_mesh_new_nomain_from_template(
|
||||
&mesh_in, mesh_in.totvert, mesh_in.totedge, selected_polys_num, selected_loops_num);
|
||||
|
||||
mesh_out->poly_offsets_for_write().drop_back(1).copy_from(new_loop_starts);
|
||||
|
||||
bke::copy_attributes(mesh_in.attributes(),
|
||||
ATTR_DOMAIN_POINT,
|
||||
propagation_info,
|
||||
{},
|
||||
mesh_out->attributes_for_write());
|
||||
bke::copy_attributes(mesh_in.attributes(),
|
||||
ATTR_DOMAIN_EDGE,
|
||||
propagation_info,
|
||||
{},
|
||||
mesh_out->attributes_for_write());
|
||||
bke::gather_attributes(mesh_in.attributes(),
|
||||
ATTR_DOMAIN_FACE,
|
||||
propagation_info,
|
||||
{},
|
||||
IndexMask::from_indices(selected_poly_indices.as_span(), memory),
|
||||
mesh_out->attributes_for_write());
|
||||
copy_face_corner_attributes(mesh_in.attributes(),
|
||||
mesh_out->attributes_for_write(),
|
||||
propagation_info,
|
||||
{},
|
||||
selected_loops_num,
|
||||
selected_poly_indices,
|
||||
mesh_in);
|
||||
|
||||
/* Positions are not changed by the operation, so the bounds are the same. */
|
||||
mesh_out->runtime->bounds_cache = mesh_in.runtime->bounds_cache;
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_DELETE_GEOMETRY_MODE_ALL:
|
||||
return geometry::mesh_copy_selection(mesh, selection, selection_domain, propagation_info);
|
||||
case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE:
|
||||
return geometry::mesh_copy_selection_keep_verts(
|
||||
mesh, selection, selection_domain, propagation_info);
|
||||
case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE:
|
||||
return geometry::mesh_copy_selection_keep_edges(
|
||||
mesh, selection, selection_domain, propagation_info);
|
||||
}
|
||||
|
||||
geometry_set.replace_mesh(mesh_out);
|
||||
}
|
||||
|
||||
static void separate_mesh_selection(GeometrySet &geometry_set,
|
||||
const Field<bool> &selection_field,
|
||||
const eAttrDomain selection_domain,
|
||||
const GeometryNodeDeleteGeometryMode mode,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const Mesh &src_mesh = *geometry_set.get_mesh_for_read();
|
||||
const bke::MeshFieldContext field_context{src_mesh, selection_domain};
|
||||
fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)};
|
||||
evaluator.add(selection_field);
|
||||
evaluator.evaluate();
|
||||
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||
/* Check if there is anything to delete. */
|
||||
if (selection.is_empty() || (selection.is_single() && selection.get_internal_single())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const VArraySpan<bool> selection_span{selection};
|
||||
|
||||
do_mesh_separation(
|
||||
geometry_set, src_mesh, selection_span, selection_domain, mode, propagation_info);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_delete_geometry_cc
|
||||
|
@ -1040,9 +146,13 @@ void separate_geometry(GeometrySet &geometry_set,
|
|||
some_valid_domain = true;
|
||||
}
|
||||
}
|
||||
if (geometry_set.has_mesh()) {
|
||||
if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
|
||||
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) {
|
||||
file_ns::separate_mesh_selection(geometry_set, selection, domain, mode, propagation_info);
|
||||
std::optional<Mesh *> dst_mesh = file_ns::separate_mesh_selection(
|
||||
*mesh, selection, domain, mode, propagation_info);
|
||||
if (dst_mesh) {
|
||||
geometry_set.replace_mesh(*dst_mesh);
|
||||
}
|
||||
some_valid_domain = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,17 +133,33 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item)
|
|||
return get_simulation_item_cpp_type(eNodeSocketDatatype(item.socket_type));
|
||||
}
|
||||
|
||||
static void remove_materials(Material ***materials, short *materials_num)
|
||||
{
|
||||
MEM_SAFE_FREE(*materials);
|
||||
*materials_num = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removess parts of the geometry that can't be stored in the simulation state:
|
||||
* - Anonymous attributes can't be stored because it is not known which of them will or will not be
|
||||
* used in the future.
|
||||
* - Materials can't be stored directly, because they are linked ID data blocks that can't be
|
||||
* restored from baked data currently.
|
||||
*/
|
||||
static void cleanup_geometry_for_simulation_state(GeometrySet &main_geometry)
|
||||
{
|
||||
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
|
||||
if (Mesh *mesh = geometry.get_mesh_for_write()) {
|
||||
mesh->attributes_for_write().remove_anonymous();
|
||||
remove_materials(&mesh->mat, &mesh->totcol);
|
||||
}
|
||||
if (Curves *curves = geometry.get_curves_for_write()) {
|
||||
curves->geometry.wrap().attributes_for_write().remove_anonymous();
|
||||
remove_materials(&curves->mat, &curves->totcol);
|
||||
}
|
||||
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
|
||||
pointcloud->attributes_for_write().remove_anonymous();
|
||||
remove_materials(&pointcloud->mat, &pointcloud->totcol);
|
||||
}
|
||||
if (bke::Instances *instances = geometry.get_instances_for_write()) {
|
||||
instances->attributes_for_write().remove_anonymous();
|
||||
|
|
|
@ -528,7 +528,7 @@ void RE_RenderBuffer_assign_shared(RenderBuffer *lhs, const RenderBuffer *rhs);
|
|||
void RE_RenderBuffer_data_free(RenderBuffer *render_buffer);
|
||||
|
||||
/* Implementation of above, but for byte buffer. */
|
||||
/* TODO(sergey): Once everything is C++ we can remove the duplicated API. */
|
||||
/* TODO(sergey): Once everything is C++ we can remove the duplicated API. */
|
||||
RenderByteBuffer RE_RenderByteBuffer_new(uint8_t *data);
|
||||
void RE_RenderByteBuffer_assign_data(RenderByteBuffer *render_buffer, uint8_t *data);
|
||||
void RE_RenderByteBuffer_assign_shared(RenderByteBuffer *lhs, const RenderByteBuffer *rhs);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue