Extern: Update json library to 3.11.2 #108519
|
@ -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" (
|
||||
|
|
|
@ -610,6 +610,7 @@ 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 +2522,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 +4511,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 +4598,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 +5117,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 +5527,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,7 @@ 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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -2097,9 +2097,7 @@ class USERPREF_PT_addons(AddOnPanel, Panel):
|
|||
split.label(text=" " + info["warning"], icon='ERROR')
|
||||
|
||||
user_addon = USERPREF_PT_addons.is_user_addon(mod, user_addon_paths)
|
||||
tot_row = bool(info["doc_url"]) + bool(user_addon)
|
||||
|
||||
if tot_row:
|
||||
if info["doc_url"] or info.get("tracker_url"):
|
||||
split = colsub.row().split(factor=0.15)
|
||||
split.label(text="Internet:")
|
||||
sub = split.row()
|
||||
|
@ -2123,10 +2121,13 @@ class USERPREF_PT_addons(AddOnPanel, Panel):
|
|||
)
|
||||
props.type = 'BUG_ADDON'
|
||||
props.id = addon_info
|
||||
if user_addon:
|
||||
sub.operator(
|
||||
"preferences.addon_remove", text="Remove", icon='CANCEL',
|
||||
).module = mod.__name__
|
||||
|
||||
if user_addon:
|
||||
split = colsub.row().split(factor=0.15)
|
||||
split.label(text="User:")
|
||||
split.operator(
|
||||
"preferences.addon_remove", text="Remove", icon='CANCEL',
|
||||
).module = mod.__name__
|
||||
|
||||
# Show addon user preferences
|
||||
if is_enabled:
|
||||
|
|
|
@ -2303,6 +2303,17 @@ class VIEW3D_MT_volume_add(Menu):
|
|||
icon='OUTLINER_DATA_VOLUME')
|
||||
|
||||
|
||||
class VIEW3D_MT_grease_pencil_add(Menu):
|
||||
bl_idname = "VIEW3D_MT_grease_pencil_add"
|
||||
bl_label = "Grease Pencil"
|
||||
|
||||
def draw(self, _context):
|
||||
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):
|
||||
bl_label = "Add"
|
||||
bl_translation_context = i18n_contexts.operator_default
|
||||
|
@ -2328,7 +2339,14 @@ class VIEW3D_MT_add(Menu):
|
|||
if context.preferences.experimental.use_new_point_cloud_type:
|
||||
layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD')
|
||||
layout.menu("VIEW3D_MT_volume_add", text="Volume", text_ctxt=i18n_contexts.id_id, icon='OUTLINER_OB_VOLUME')
|
||||
layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
|
||||
if context.preferences.experimental.use_grease_pencil_version3:
|
||||
layout.menu("VIEW3D_MT_grease_pencil_add", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
|
||||
else:
|
||||
layout.operator_menu_enum(
|
||||
"object.gpencil_add",
|
||||
"type",
|
||||
text="Grease Pencil",
|
||||
icon='OUTLINER_OB_GREASEPENCIL')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -8188,6 +8206,7 @@ classes = (
|
|||
VIEW3D_MT_lightprobe_add,
|
||||
VIEW3D_MT_camera_add,
|
||||
VIEW3D_MT_volume_add,
|
||||
VIEW3D_MT_grease_pencil_add,
|
||||
VIEW3D_MT_add,
|
||||
VIEW3D_MT_image_add,
|
||||
VIEW3D_MT_object,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -18,6 +18,13 @@
|
|||
#include "DNA_gpencil_legacy_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
struct Main;
|
||||
struct Depsgraph;
|
||||
struct BoundBox;
|
||||
struct Scene;
|
||||
struct Object;
|
||||
struct Material;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
namespace greasepencil {
|
||||
|
@ -196,7 +203,6 @@ class Layer : public ::GreasePencilLayer {
|
|||
* \returns true on success.
|
||||
*/
|
||||
bool insert_frame(int frame_number, const GreasePencilFrame &frame);
|
||||
bool insert_frame(int frame_number, GreasePencilFrame &&frame);
|
||||
|
||||
/**
|
||||
* Inserts the frame into the layer. If there exists a frame at \a frame_number already, it is
|
||||
|
@ -204,7 +210,6 @@ class Layer : public ::GreasePencilLayer {
|
|||
* \returns true on success.
|
||||
*/
|
||||
bool overwrite_frame(int frame_number, const GreasePencilFrame &frame);
|
||||
bool overwrite_frame(int frame_number, GreasePencilFrame &&frame);
|
||||
|
||||
/**
|
||||
* Returns the sorted (start) frame numbers of the frames of this layer.
|
||||
|
@ -365,12 +370,6 @@ inline bool GreasePencil::has_active_layer() const
|
|||
return (this->active_layer != nullptr);
|
||||
}
|
||||
|
||||
struct Main;
|
||||
struct Depsgraph;
|
||||
struct BoundBox;
|
||||
struct Scene;
|
||||
struct Object;
|
||||
|
||||
void *BKE_grease_pencil_add(Main *bmain, const char *name);
|
||||
GreasePencil *BKE_grease_pencil_new_nomain();
|
||||
BoundBox *BKE_grease_pencil_boundbox_get(Object *ob);
|
||||
|
@ -378,5 +377,15 @@ void BKE_grease_pencil_data_update(struct Depsgraph *depsgraph,
|
|||
struct Scene *scene,
|
||||
struct Object *object);
|
||||
|
||||
int BKE_grease_pencil_object_material_index_get_by_name(Object *ob, const char *name);
|
||||
Material *BKE_grease_pencil_object_material_new(Main *bmain,
|
||||
Object *ob,
|
||||
const char *name,
|
||||
int *r_index);
|
||||
Material *BKE_grease_pencil_object_material_ensure_by_name(Main *bmain,
|
||||
Object *ob,
|
||||
const char *name,
|
||||
int *r_index);
|
||||
|
||||
bool BKE_grease_pencil_references_cyclic_check(const GreasePencil *id_reference,
|
||||
const GreasePencil *grease_pencil);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_query.h"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
@ -408,24 +409,12 @@ bool Layer::insert_frame(int frame_number, const GreasePencilFrame &frame)
|
|||
return this->frames_for_write().add(frame_number, frame);
|
||||
}
|
||||
|
||||
bool Layer::insert_frame(int frame_number, GreasePencilFrame &&frame)
|
||||
{
|
||||
this->tag_frames_map_changed();
|
||||
return this->frames_for_write().add(frame_number, frame);
|
||||
}
|
||||
|
||||
bool Layer::overwrite_frame(int frame_number, const GreasePencilFrame &frame)
|
||||
{
|
||||
this->tag_frames_map_changed();
|
||||
return this->frames_for_write().add_overwrite(frame_number, frame);
|
||||
}
|
||||
|
||||
bool Layer::overwrite_frame(int frame_number, GreasePencilFrame &&frame)
|
||||
{
|
||||
this->tag_frames_map_changed();
|
||||
return this->frames_for_write().add_overwrite(frame_number, std::move(frame));
|
||||
}
|
||||
|
||||
Span<int> Layer::sorted_keys() const
|
||||
{
|
||||
this->runtime->sorted_keys_cache_.ensure([&](Vector<int> &r_data) {
|
||||
|
@ -495,13 +484,13 @@ LayerGroup::LayerGroup(const LayerGroup &other) : LayerGroup()
|
|||
switch (child->type) {
|
||||
case GP_LAYER_TREE_LEAF: {
|
||||
GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
|
||||
Layer *dup_layer = new Layer(layer->wrap());
|
||||
Layer *dup_layer = MEM_new<Layer>(__func__, layer->wrap());
|
||||
this->add_layer(dup_layer);
|
||||
break;
|
||||
}
|
||||
case GP_LAYER_TREE_GROUP: {
|
||||
GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
|
||||
LayerGroup *dup_group = new LayerGroup(group->wrap());
|
||||
LayerGroup *dup_group = MEM_new<LayerGroup>(__func__, group->wrap());
|
||||
this->add_group(dup_group);
|
||||
break;
|
||||
}
|
||||
|
@ -517,12 +506,12 @@ LayerGroup::~LayerGroup()
|
|||
switch (child->type) {
|
||||
case GP_LAYER_TREE_LEAF: {
|
||||
GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
|
||||
layer->wrap().~Layer();
|
||||
MEM_delete(&layer->wrap());
|
||||
break;
|
||||
}
|
||||
case GP_LAYER_TREE_GROUP: {
|
||||
GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
|
||||
group->wrap().~LayerGroup();
|
||||
MEM_delete(&group->wrap());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -543,7 +532,7 @@ LayerGroup &LayerGroup::add_group(LayerGroup *group)
|
|||
|
||||
LayerGroup &LayerGroup::add_group(StringRefNull name)
|
||||
{
|
||||
LayerGroup *new_group = new LayerGroup(name);
|
||||
LayerGroup *new_group = MEM_new<LayerGroup>(__func__, name);
|
||||
return this->add_group(new_group);
|
||||
}
|
||||
|
||||
|
@ -558,7 +547,7 @@ Layer &LayerGroup::add_layer(Layer *layer)
|
|||
|
||||
Layer &LayerGroup::add_layer(StringRefNull name)
|
||||
{
|
||||
Layer *new_layer = new Layer(name);
|
||||
Layer *new_layer = MEM_new<Layer>(__func__, name);
|
||||
return this->add_layer(new_layer);
|
||||
}
|
||||
|
||||
|
@ -754,6 +743,56 @@ void BKE_grease_pencil_data_update(struct Depsgraph * /*depsgraph*/,
|
|||
|
||||
/** \} */
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
/** \name Grease Pencil material functions
|
||||
* \{ */
|
||||
|
||||
int BKE_grease_pencil_object_material_index_get_by_name(Object *ob, const char *name)
|
||||
{
|
||||
short *totcol = BKE_object_material_len_p(ob);
|
||||
Material *read_ma = NULL;
|
||||
for (short i = 0; i < *totcol; i++) {
|
||||
read_ma = BKE_object_material_get(ob, i + 1);
|
||||
if (STREQ(name, read_ma->id.name + 2)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Material *BKE_grease_pencil_object_material_new(Main *bmain,
|
||||
Object *ob,
|
||||
const char *name,
|
||||
int *r_index)
|
||||
{
|
||||
Material *ma = BKE_gpencil_material_add(bmain, name);
|
||||
id_us_min(&ma->id); /* no users yet */
|
||||
|
||||
BKE_object_material_slot_add(bmain, ob);
|
||||
BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
|
||||
|
||||
if (r_index) {
|
||||
*r_index = ob->actcol - 1;
|
||||
}
|
||||
return ma;
|
||||
}
|
||||
|
||||
Material *BKE_grease_pencil_object_material_ensure_by_name(Main *bmain,
|
||||
Object *ob,
|
||||
const char *name,
|
||||
int *r_index)
|
||||
{
|
||||
int index = BKE_grease_pencil_object_material_index_get_by_name(ob, name);
|
||||
if (index != -1) {
|
||||
*r_index = index;
|
||||
return BKE_object_material_get(ob, index + 1);
|
||||
}
|
||||
return BKE_grease_pencil_object_material_new(bmain, ob, name, r_index);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
/** \name Grease Pencil reference functions
|
||||
* \{ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -3053,7 +3053,10 @@ void blo_do_versions_280(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
MEM_callocN(sizeof(ARegion), "navigation bar for properties"));
|
||||
ARegion *region_header = nullptr;
|
||||
|
||||
LISTBASE_FOREACH (ARegion *, region_header, regionbase) {
|
||||
for (region_header = static_cast<ARegion *>(regionbase->first);
|
||||
region_header != nullptr;
|
||||
region_header = static_cast<ARegion *>(region_header->next))
|
||||
{
|
||||
if (region_header->regiontype == RGN_TYPE_HEADER) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -861,6 +861,7 @@ class Texture : NonCopyable {
|
|||
}
|
||||
GPU_TEXTURE_FREE_SAFE(stencil_view_);
|
||||
mip_views_.clear();
|
||||
layer_views_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@ if(WITH_BLENDER)
|
|||
add_subdirectory(geometry)
|
||||
add_subdirectory(gizmo_library)
|
||||
add_subdirectory(gpencil_legacy)
|
||||
add_subdirectory(grease_pencil)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(lattice)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
../include
|
||||
../../blenkernel
|
||||
../../blenlib
|
||||
../../blentranslation
|
||||
../../depsgraph
|
||||
../../gpu
|
||||
../../imbuf
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
../../../../intern/guardedalloc
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/grease_pencil_add.cc
|
||||
)
|
||||
|
||||
set(LIB
|
||||
bf_blenkernel
|
||||
bf_blenlib
|
||||
)
|
||||
|
||||
blender_add_lib(bf_editor_grease_pencil "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_dependencies(bf_editor_curves bf_rna)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct Main;
|
||||
struct Object;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -70,6 +70,7 @@ set(LIB
|
|||
bf_blenkernel
|
||||
bf_blenlib
|
||||
bf_editor_mesh
|
||||
bf_editor_grease_pencil
|
||||
bf_render
|
||||
bf_windowmanager
|
||||
)
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
#include "ED_curve.h"
|
||||
#include "ED_curves.h"
|
||||
#include "ED_gpencil_legacy.h"
|
||||
#include "ED_grease_pencil.h"
|
||||
#include "ED_mball.h"
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_node.h"
|
||||
|
@ -1306,7 +1307,7 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot)
|
|||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Add Gpencil Operator
|
||||
/** \name Add Gpencil (legacy) Operator
|
||||
* \{ */
|
||||
|
||||
static bool object_gpencil_add_poll(bContext *C)
|
||||
|
@ -1529,8 +1530,8 @@ static EnumPropertyItem rna_enum_gpencil_add_stroke_depth_order_items[] = {
|
|||
void OBJECT_OT_gpencil_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Grease Pencil";
|
||||
ot->description = "Add a Grease Pencil object to the scene";
|
||||
ot->name = "Add Grease Pencil (legacy)";
|
||||
ot->description = "Add a Grease Pencil (legacy) object to the scene";
|
||||
ot->idname = "OBJECT_OT_gpencil_add";
|
||||
|
||||
/* api callbacks */
|
||||
|
@ -1576,6 +1577,118 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Add Grease Pencil Operator
|
||||
* \{ */
|
||||
|
||||
static int object_grease_pencil_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
using namespace blender::ed;
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
/* TODO: For now, only support adding the 'Stroke' type. */
|
||||
const int type = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
ushort local_view_bits;
|
||||
float loc[3], rot[3];
|
||||
|
||||
/* NOTE: We use 'Y' here (not 'Z'), as. */
|
||||
WM_operator_view3d_unit_defaults(C, op);
|
||||
if (!ED_object_add_generic_get_opts(
|
||||
C, op, 'Y', loc, rot, nullptr, nullptr, &local_view_bits, nullptr))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const char *ob_name = nullptr;
|
||||
switch (type) {
|
||||
case GP_EMPTY: {
|
||||
ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "GPencil");
|
||||
break;
|
||||
}
|
||||
case GP_STROKE: {
|
||||
ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Stroke");
|
||||
break;
|
||||
}
|
||||
case GP_MONKEY: {
|
||||
ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Suzanne");
|
||||
break;
|
||||
}
|
||||
case GP_LRT_OBJECT:
|
||||
case GP_LRT_SCENE:
|
||||
case GP_LRT_COLLECTION: {
|
||||
ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "LineArt");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Object *object = ED_object_add_type(
|
||||
C, OB_GREASE_PENCIL, ob_name, loc, rot, false, local_view_bits);
|
||||
GreasePencil &grease_pencil_id = *static_cast<GreasePencil *>(object->data);
|
||||
switch (type) {
|
||||
case GP_EMPTY: {
|
||||
greasepencil::create_blank(*bmain, *object, scene->r.cfra);
|
||||
break;
|
||||
}
|
||||
case GP_STROKE: {
|
||||
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_stroke(*bmain, *object, mat, scene->r.cfra);
|
||||
break;
|
||||
}
|
||||
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: {
|
||||
/* TODO. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&grease_pencil_id.id, ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil_id.id);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_grease_pencil_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Grease Pencil";
|
||||
ot->description = "Add a Grease Pencil object to the scene";
|
||||
ot->idname = "OBJECT_OT_grease_pencil_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = object_grease_pencil_add_exec;
|
||||
ot->poll = ED_operator_objectmode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", "");
|
||||
|
||||
ED_object_add_unit_props_radius(ot);
|
||||
ED_object_add_generic_props(ot, false);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Add Light Operator
|
||||
* \{ */
|
||||
|
|
|
@ -119,6 +119,7 @@ void OBJECT_OT_empty_add(struct wmOperatorType *ot);
|
|||
void OBJECT_OT_lightprobe_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_drop_named_image(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_gpencil_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_grease_pencil_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_light_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_effector_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_camera_add(struct wmOperatorType *ot);
|
||||
|
|
|
@ -90,6 +90,7 @@ void ED_operatortypes_object(void)
|
|||
WM_operatortype_append(OBJECT_OT_lightprobe_add);
|
||||
WM_operatortype_append(OBJECT_OT_drop_named_image);
|
||||
WM_operatortype_append(OBJECT_OT_gpencil_add);
|
||||
WM_operatortype_append(OBJECT_OT_grease_pencil_add);
|
||||
WM_operatortype_append(OBJECT_OT_light_add);
|
||||
WM_operatortype_append(OBJECT_OT_camera_add);
|
||||
WM_operatortype_append(OBJECT_OT_speaker_add);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -389,7 +389,8 @@ static int palette_color_add_exec(bContext *C, wmOperator * /*op*/)
|
|||
PAINT_MODE_TEXTURE_3D,
|
||||
PAINT_MODE_TEXTURE_2D,
|
||||
PAINT_MODE_VERTEX,
|
||||
PAINT_MODE_SCULPT)) {
|
||||
PAINT_MODE_SCULPT))
|
||||
{
|
||||
copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush));
|
||||
color->value = 0.0;
|
||||
}
|
||||
|
@ -1540,6 +1541,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);
|
||||
|
|
|
@ -57,7 +57,9 @@ 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
|
||||
tree/tree_element_label.cc
|
||||
tree/tree_element_nla.cc
|
||||
|
@ -77,7 +79,9 @@ 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
|
||||
tree/tree_element_label.hh
|
||||
tree/tree_element_nla.hh
|
||||
|
|
|
@ -551,39 +551,14 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
|
|||
switch (GS(id->name)) {
|
||||
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_ME: {
|
||||
Mesh *me = (Mesh *)id;
|
||||
|
||||
if (outliner_animdata_test(me->adt)) {
|
||||
outliner_add_element(space_outliner, &te->subtree, me, te, TSE_ANIM_DATA, 0);
|
||||
}
|
||||
|
||||
outliner_add_element(space_outliner, &te->subtree, me->key, te, TSE_SOME_ID, 0);
|
||||
for (int a = 0; a < me->totcol; a++) {
|
||||
outliner_add_element(space_outliner, &te->subtree, me->mat[a], te, TSE_SOME_ID, a);
|
||||
}
|
||||
/* could do tfaces with image links, but the images are not grouped nicely.
|
||||
* would require going over all tfaces, sort images in use. etc... */
|
||||
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,7 +20,9 @@
|
|||
|
||||
#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"
|
||||
|
||||
#include "tree_element_id.hh"
|
||||
|
@ -39,9 +41,11 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
|
|||
return std::make_unique<TreeElementIDLibrary>(legacy_te, (Library &)id);
|
||||
case ID_SCE:
|
||||
return std::make_unique<TreeElementIDScene>(legacy_te, (Scene &)id);
|
||||
case ID_OB:
|
||||
case ID_ME:
|
||||
return std::make_unique<TreeElementIDMesh>(legacy_te, (Mesh &)id);
|
||||
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
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_outliner_types.h"
|
||||
|
||||
#include "../outliner_intern.hh"
|
||||
|
||||
#include "tree_element_id_mesh.hh"
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
TreeElementIDMesh::TreeElementIDMesh(TreeElement &legacy_te_, Mesh &mesh)
|
||||
: TreeElementID(legacy_te_, mesh.id), mesh_(mesh)
|
||||
{
|
||||
}
|
||||
|
||||
bool TreeElementIDMesh::isExpandValid() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void TreeElementIDMesh::expand(SpaceOutliner &space_outliner) const
|
||||
{
|
||||
expand_animation_data(space_outliner, mesh_.adt);
|
||||
|
||||
expandKey(space_outliner);
|
||||
expandMaterials(space_outliner);
|
||||
}
|
||||
|
||||
void TreeElementIDMesh::expandKey(SpaceOutliner &space_outliner) const
|
||||
{
|
||||
outliner_add_element(
|
||||
&space_outliner, &legacy_te_.subtree, mesh_.key, &legacy_te_, TSE_SOME_ID, 0);
|
||||
}
|
||||
|
||||
void TreeElementIDMesh::expandMaterials(SpaceOutliner &space_outliner) const
|
||||
{
|
||||
for (int a = 0; a < mesh_.totcol; a++) {
|
||||
outliner_add_element(
|
||||
&space_outliner, &legacy_te_.subtree, mesh_.mat[a], &legacy_te_, TSE_SOME_ID, a);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::outliner
|
|
@ -0,0 +1,27 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tree_element_id.hh"
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
class TreeElementIDMesh final : public TreeElementID {
|
||||
Mesh &mesh_;
|
||||
|
||||
public:
|
||||
TreeElementIDMesh(TreeElement &legacy_te_, Mesh &mesh);
|
||||
|
||||
void expand(SpaceOutliner &) const override;
|
||||
bool isExpandValid() const override;
|
||||
|
||||
private:
|
||||
void expandKey(SpaceOutliner &) const;
|
||||
void expandMaterials(SpaceOutliner &) const;
|
||||
};
|
||||
|
||||
} // namespace blender::ed::outliner
|
|
@ -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;
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "PIL_time.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "ED_image.h"
|
||||
|
@ -1190,7 +1191,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||
|
||||
bool only_selected_faces = params->only_selected_faces;
|
||||
bool only_selected_uvs = params->only_selected_uvs;
|
||||
const bool ignore_pinned = params->pin_method == ED_UVPACK_PIN_IGNORED;
|
||||
const bool ignore_pinned = params->pin_method == ED_UVPACK_PIN_IGNORE;
|
||||
if (ignore_pinned && params->pin_unselected) {
|
||||
only_selected_faces = false;
|
||||
only_selected_uvs = false;
|
||||
|
@ -1502,11 +1503,17 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
pack_island_params.setFromUnwrapOptions(options);
|
||||
pack_island_params.rotate_method = eUVPackIsland_RotationMethod(
|
||||
RNA_enum_get(op->ptr, "rotate_method"));
|
||||
if (RNA_boolean_get(op->ptr, "rotate")) {
|
||||
pack_island_params.rotate_method = eUVPackIsland_RotationMethod(
|
||||
RNA_enum_get(op->ptr, "rotate_method"));
|
||||
}
|
||||
else {
|
||||
pack_island_params.rotate_method = ED_UVPACK_ROTATION_NONE;
|
||||
}
|
||||
pack_island_params.scale_to_fit = RNA_boolean_get(op->ptr, "scale");
|
||||
pack_island_params.merge_overlap = RNA_boolean_get(op->ptr, "merge_overlap");
|
||||
pack_island_params.pin_method = eUVPackIsland_PinMethod(RNA_enum_get(op->ptr, "pin_method"));
|
||||
pack_island_params.pin_method = eUVPackIsland_PinMethod(RNA_enum_get(op->ptr, "pin"));
|
||||
|
||||
pack_island_params.margin_method = eUVPackIsland_MarginMethod(
|
||||
RNA_enum_get(op->ptr, "margin_method"));
|
||||
pack_island_params.margin = RNA_float_get(op->ptr, "margin");
|
||||
|
@ -1562,7 +1569,6 @@ static const EnumPropertyItem pack_margin_method_items[] = {
|
|||
};
|
||||
|
||||
static const EnumPropertyItem pack_rotate_method_items[] = {
|
||||
{ED_UVPACK_ROTATION_NONE, "NONE", 0, "No rotation", "No rotation is applied to the islands"},
|
||||
RNA_ENUM_ITEM_SEPR,
|
||||
{ED_UVPACK_ROTATION_AXIS_ALIGNED,
|
||||
"AXIS_ALIGNED",
|
||||
|
@ -1579,27 +1585,48 @@ static const EnumPropertyItem pack_rotate_method_items[] = {
|
|||
};
|
||||
|
||||
static const EnumPropertyItem pack_shape_method_items[] = {
|
||||
{ED_UVPACK_SHAPE_CONCAVE, "CONCAVE", 0, "Exact shape (Concave)", "Uses exact geometry"},
|
||||
{ED_UVPACK_SHAPE_CONVEX, "CONVEX", 0, "Boundary shape (Convex)", "Uses convex hull"},
|
||||
{ED_UVPACK_SHAPE_CONCAVE, "CONCAVE", 0, "Exact Shape (Concave)", "Uses exact geometry"},
|
||||
{ED_UVPACK_SHAPE_CONVEX, "CONVEX", 0, "Boundary Shape (Convex)", "Uses convex hull"},
|
||||
RNA_ENUM_ITEM_SEPR,
|
||||
{ED_UVPACK_SHAPE_AABB, "AABB", 0, "Bounding box", "Uses bounding boxes"},
|
||||
{ED_UVPACK_SHAPE_AABB, "AABB", 0, "Bounding Box", "Uses bounding boxes"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem pinned_islands_method_items[] = {
|
||||
{ED_UVPACK_PIN_DEFAULT, "DEFAULT", 0, "Default", "Pin information is not used"},
|
||||
{ED_UVPACK_PIN_IGNORED, "IGNORED", 0, "Ignored", "Pinned islands are not packed"},
|
||||
{ED_UVPACK_PIN_LOCK_SCALE, "SCALE", 0, "Locked scale", "Pinned islands won't rescale"},
|
||||
{ED_UVPACK_PIN_LOCK_ROTATION, "ROTATION", 0, "Locked rotation", "Pinned islands won't rotate"},
|
||||
{ED_UVPACK_PIN_PACK, "PACK", 0, "Pack", "Pinned islands are packed normally"},
|
||||
{ED_UVPACK_PIN_LOCK_SCALE, "SCALE", 0, "Lock Scale", "Pinned islands won't rescale"},
|
||||
{ED_UVPACK_PIN_LOCK_ROTATION, "ROTATION", 0, "Lock Rotation", "Pinned islands won't rotate"},
|
||||
{ED_UVPACK_PIN_LOCK_ROTATION_SCALE,
|
||||
"ROTATION_SCALE",
|
||||
0,
|
||||
"Locked rotation and scale",
|
||||
"Lock Rotation and Scale",
|
||||
"Pinned islands will translate only"},
|
||||
{ED_UVPACK_PIN_LOCK_ALL, "LOCKED", 0, "Locked position", "Pinned islands are locked in place"},
|
||||
{ED_UVPACK_PIN_LOCK_ALL, "LOCKED", 0, "Lock in Place", "Pinned islands are locked in place"},
|
||||
{ED_UVPACK_PIN_IGNORE, "IGNORE", 0, "Ignore", "Pinned islands are not packed"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static void uv_pack_islands_ui(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
uiLayout *layout = op->layout;
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
uiItemR(layout, op->ptr, "shape_method", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "scale", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "rotate", 0, nullptr, ICON_NONE);
|
||||
uiLayout *sub = uiLayoutRow(layout, true);
|
||||
uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "rotate"));
|
||||
uiItemR(sub, op->ptr, "rotate_method", 0, nullptr, ICON_NONE);
|
||||
uiItemS(layout);
|
||||
uiItemR(layout, op->ptr, "margin_method", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "margin", 0, nullptr, ICON_NONE);
|
||||
uiItemS(layout);
|
||||
uiItemR(layout, op->ptr, "pin", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "merge_overlap", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "udim_source", 0, nullptr, ICON_NONE);
|
||||
uiItemS(layout);
|
||||
}
|
||||
|
||||
void UV_OT_pack_islands(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem pack_target[] = {
|
||||
|
@ -1637,10 +1664,12 @@ void UV_OT_pack_islands(wmOperatorType *ot)
|
|||
#else
|
||||
ot->invoke = WM_operator_props_popup_confirm;
|
||||
#endif
|
||||
ot->ui = uv_pack_islands_ui;
|
||||
ot->poll = ED_operator_uvedit;
|
||||
|
||||
/* properties */
|
||||
RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", "");
|
||||
RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands to improve layout");
|
||||
RNA_def_enum(ot->srna,
|
||||
"rotate_method",
|
||||
pack_rotate_method_items,
|
||||
|
@ -1649,7 +1678,7 @@ void UV_OT_pack_islands(wmOperatorType *ot)
|
|||
"");
|
||||
RNA_def_boolean(ot->srna, "scale", true, "Scale", "Scale islands to fill unit square");
|
||||
RNA_def_boolean(
|
||||
ot->srna, "merge_overlap", false, "Merge Overlapped", "Overlapping islands stick together");
|
||||
ot->srna, "merge_overlap", false, "Merge Overlapping", "Overlapping islands stick together");
|
||||
RNA_def_enum(ot->srna,
|
||||
"margin_method",
|
||||
pack_margin_method_items,
|
||||
|
@ -1658,12 +1687,8 @@ void UV_OT_pack_islands(wmOperatorType *ot)
|
|||
"");
|
||||
RNA_def_float_factor(
|
||||
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
|
||||
RNA_def_enum(ot->srna,
|
||||
"pin_method",
|
||||
pinned_islands_method_items,
|
||||
ED_UVPACK_PIN_DEFAULT,
|
||||
"Pinned Islands",
|
||||
"");
|
||||
RNA_def_enum(
|
||||
ot->srna, "pin", pinned_islands_method_items, ED_UVPACK_PIN_PACK, "Pinned Islands", "");
|
||||
RNA_def_enum(ot->srna,
|
||||
"shape_method",
|
||||
pack_shape_method_items,
|
||||
|
@ -2385,7 +2410,7 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len
|
|||
blender::geometry::UVPackIsland_Params pack_island_params;
|
||||
pack_island_params.setFromUnwrapOptions(options);
|
||||
pack_island_params.rotate_method = ED_UVPACK_ROTATION_ANY;
|
||||
pack_island_params.pin_method = ED_UVPACK_PIN_IGNORED;
|
||||
pack_island_params.pin_method = ED_UVPACK_PIN_IGNORE;
|
||||
pack_island_params.margin_method = ED_UVPACK_MARGIN_SCALED;
|
||||
pack_island_params.margin = scene->toolsettings->uvcalc_margin;
|
||||
|
||||
|
@ -2527,7 +2552,7 @@ static int unwrap_exec(bContext *C, wmOperator *op)
|
|||
blender::geometry::UVPackIsland_Params pack_island_params;
|
||||
pack_island_params.setFromUnwrapOptions(options);
|
||||
pack_island_params.rotate_method = ED_UVPACK_ROTATION_ANY;
|
||||
pack_island_params.pin_method = ED_UVPACK_PIN_IGNORED;
|
||||
pack_island_params.pin_method = ED_UVPACK_PIN_IGNORE;
|
||||
pack_island_params.margin_method = eUVPackIsland_MarginMethod(
|
||||
RNA_enum_get(op->ptr, "margin_method"));
|
||||
pack_island_params.margin = RNA_float_get(op->ptr, "margin");
|
||||
|
|
|
@ -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
|
|
@ -49,12 +49,11 @@ enum eUVPackIsland_ShapeMethod {
|
|||
};
|
||||
|
||||
enum eUVPackIsland_PinMethod {
|
||||
ED_UVPACK_PIN_IGNORED = 0,
|
||||
ED_UVPACK_PIN_DEFAULT,
|
||||
ED_UVPACK_PIN_IGNORE = 0,
|
||||
ED_UVPACK_PIN_PACK,
|
||||
ED_UVPACK_PIN_LOCK_ROTATION,
|
||||
ED_UVPACK_PIN_LOCK_ROTATION_SCALE,
|
||||
ED_UVPACK_PIN_LOCK_SCALE,
|
||||
ED_UVPACK_PIN_LOCK_TRANSLATION,
|
||||
ED_UVPACK_PIN_LOCK_ALL, /* Lock translation, rotation and scale. */
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -339,7 +339,7 @@ UVPackIsland_Params::UVPackIsland_Params()
|
|||
only_selected_faces = false;
|
||||
use_seams = false;
|
||||
correct_aspect = false;
|
||||
pin_method = ED_UVPACK_PIN_DEFAULT;
|
||||
pin_method = ED_UVPACK_PIN_PACK;
|
||||
pin_unselected = false;
|
||||
merge_overlap = false;
|
||||
margin = 0.001f;
|
||||
|
@ -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,
|
||||
|
@ -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};
|
||||
|
||||
|
@ -1981,7 +1981,6 @@ bool PackIsland::can_translate_(const UVPackIsland_Params ¶ms) const
|
|||
}
|
||||
switch (params.pin_method) {
|
||||
case ED_UVPACK_PIN_LOCK_ALL:
|
||||
case ED_UVPACK_PIN_LOCK_TRANSLATION:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3258,6 +3258,31 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (RNA_boolean_get(op->ptr, "incremental")) {
|
||||
char head[FILE_MAXFILE], tail[FILE_MAXFILE];
|
||||
ushort digits;
|
||||
int num = BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &digits);
|
||||
/* Numbers greater than INT_MAX return 0, resulting in always appending "1" to the name. */
|
||||
if (num == 0 && digits == 0) {
|
||||
/* This does nothing if there are no numbers at the end of the head. */
|
||||
BLI_str_rstrip_digits(head);
|
||||
}
|
||||
|
||||
const int tries_limit = 1000;
|
||||
int tries = 0;
|
||||
bool in_use = true;
|
||||
do {
|
||||
num++;
|
||||
tries++;
|
||||
BLI_path_sequence_encode(filepath, sizeof(filepath), head, tail, digits, num);
|
||||
in_use = BLI_exists(filepath);
|
||||
} while (in_use && tries < tries_limit && num < INT_MAX);
|
||||
if (in_use) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Unable to find an available incremented file name");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
const int fileflags_orig = G.fileflags;
|
||||
int fileflags = G.fileflags;
|
||||
|
||||
|
@ -3392,6 +3417,18 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *
|
|||
return ret;
|
||||
}
|
||||
|
||||
static char *wm_save_mainfile_get_description(bContext * /*C*/,
|
||||
wmOperatorType * /*ot*/,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
if (RNA_boolean_get(ptr, "incremental")) {
|
||||
return BLI_strdup(
|
||||
TIP_("Save the current Blender file with a numerically incremented name that does not "
|
||||
"overwrite any existing files"));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WM_OT_save_mainfile(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Save Blender File";
|
||||
|
@ -3420,6 +3457,14 @@ void WM_OT_save_mainfile(wmOperatorType *ot)
|
|||
|
||||
prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
|
||||
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"incremental",
|
||||
false,
|
||||
"Incremental",
|
||||
"Save the current Blender file with a numerically incremented name that "
|
||||
"does not overwrite any existing files");
|
||||
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
Loading…
Reference in New Issue