Extern: Update json library to 3.11.2 #108519

Merged
Julian Eisel merged 6 commits from JulianEisel/blender:temp-json-update into main 2023-07-11 18:01:25 +02:00
51 changed files with 2926 additions and 1152 deletions
Showing only changes of commit 464092453c - Show all commits

View File

@ -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}

View File

@ -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" (

View File

@ -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},

View File

@ -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

View File

@ -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'

View File

@ -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:

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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);

View File

@ -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
* \{ */

View File

@ -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.

View File

@ -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;

View File

@ -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.
*

View File

@ -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

View File

@ -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) {

View File

@ -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;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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;
}

View File

@ -861,6 +861,7 @@ class Texture : NonCopyable {
}
GPU_TEXTURE_FREE_SAFE(stencil_view_);
mip_views_.clear();
layer_views_.clear();
}
/**

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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,

View File

@ -70,6 +70,7 @@ set(LIB
bf_blenkernel
bf_blenlib
bf_editor_mesh
bf_editor_grease_pencil
bf_render
bf_windowmanager
)

View File

@ -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
* \{ */

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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. */
};

View File

@ -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 &params_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, &params_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

View File

@ -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 &params,
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 &params,
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 &params,
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(),
[&params, &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(),
[&params, &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 &params) const
}
switch (params.pin_method) {
case ED_UVPACK_PIN_LOCK_ALL:
case ED_UVPACK_PIN_LOCK_TRANSLATION:
return false;
default:
return true;

View File

@ -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;
/**

View File

@ -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;
}
}

View File

@ -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));
}
/** \} */