Node Editor: Add overlay to automatically label reroute nodes #113368

Merged
Jacques Lucke merged 45 commits from lone_noel/blender:node-editor-reroute-label-propagation into main 2024-06-05 10:02:51 +02:00
64 changed files with 713 additions and 371 deletions
Showing only changes of commit edbb79c7cc - Show all commits

View File

@ -506,7 +506,8 @@ static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid,
openvdb::math::Extrema extrema;
openvdb::Vec3d voxel_size;
/* External .vdb files have a vec3 type for velocity, but the Blender exporter creates a vec4. */
/* External `.vdb` files have a vec3 type for velocity,
* but the Blender exporter creates a vec4. */
if (grid->isType<openvdb::Vec3fGrid>()) {
openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid);
extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());

View File

@ -475,11 +475,11 @@ extern void GHOST_setNDOFDeadZone(float deadzone);
#endif
/***************************************************************************************
* Drag'n'drop operations
* Drag & drop operations
***************************************************************************************/
/**
* Tells if the ongoing drag'n'drop object can be accepted upon mouse drop
* Tells if the ongoing drag & drop object can be accepted upon mouse drop.
*/
extern void GHOST_setAcceptDragOperation(GHOST_WindowHandle windowhandle, bool can_accept);

View File

@ -139,7 +139,7 @@ class GHOST_IWindow {
virtual void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const = 0;
/**
* Tells if the ongoing drag'n'drop object can be accepted upon mouse drop
* Tells if the ongoing drag & drop object can be accepted upon mouse drop
*/
virtual void setAcceptDragOperation(bool canAccept) = 0;

View File

@ -18,16 +18,16 @@
* The dragging sequence is performed in four phases:
*
* - Start sequence (GHOST_kEventDraggingEntered) that tells
* a drag'n'drop operation has started.
* a drag & drop operation has started.
* Already gives the object data type, and the entering mouse location
*
* - Update mouse position (GHOST_kEventDraggingUpdated) sent upon each mouse move until the
* drag'n'drop operation stops, to give the updated mouse position.
* drag & drop operation stops, to give the updated mouse position.
* Useful to highlight a potential destination, and update the status
* (through GHOST_setAcceptDragOperation) telling if the object can be dropped at the current
* cursor position.
*
* - Abort drag'n'drop sequence (GHOST_kEventDraggingExited)
* - Abort drag & drop sequence (#GHOST_kEventDraggingExited)
* sent when the user moved the mouse outside the window.
*
* - Send the dropped data (GHOST_kEventDraggingDropDone)

View File

@ -140,8 +140,8 @@ class GHOST_SystemCocoa : public GHOST_System {
bool handleOpenDocumentRequest(void *filepathStr);
/**
* Handles a drag'n'drop destination event. Called by GHOST_WindowCocoa window subclass
* \param eventType: The type of drag'n'drop event.
* Handles a drag & drop destination event. Called by GHOST_WindowCocoa window subclass.
* \param eventType: The type of drag & drop event.
* \param draggedObjectType: The type object concerned.
* (currently array of file names, string, TIFF image).
* \param mouseX: x mouse coordinate (in cocoa base window coordinates).

View File

@ -264,9 +264,9 @@ class GHOST_SystemWin32 : public GHOST_System {
GHOST_DialogOptions dialog_options) const;
/**
* Creates a drag'n'drop event and pushes it immediately onto the event queue.
* Called by GHOST_DropTargetWin32 class.
* \param eventType: The type of drag'n'drop event
* Creates a drag & drop event and pushes it immediately onto the event queue.
* Called by #GHOST_DropTargetWin32 class.
* \param eventType: The type of drag & drop event
* \param draggedObjectType: The type object concerned
* (currently array of file names, string, ?bitmap)
* \param mouseX: x mouse coordinate (in window coordinates)

View File

@ -246,9 +246,9 @@ class GHOST_SystemX11 : public GHOST_System {
GHOST_DialogOptions dialog_options) const override;
#ifdef WITH_XDND
/**
* Creates a drag'n'drop event and pushes it immediately onto the event queue.
* Called by GHOST_DropTargetX11 class.
* \param eventType: The type of drag'n'drop event.
* Creates a drag & drop event and pushes it immediately onto the event queue.
* Called by #GHOST_DropTargetX11 class.
* \param eventType: The type of drag & drop event.
* \param draggedObjectType: The type object concerned.
* (currently array of file names, string, ?bitmap)
* \param mouseX: x mouse coordinate (in window coordinates).

View File

@ -200,7 +200,7 @@ class GHOST_Window : public GHOST_IWindow {
GHOST_TSuccess getSwapInterval(int &intervalOut) override;
/**
* Tells if the ongoing drag'n'drop object can be accepted upon mouse drop
* Tells if the ongoing drag & drop object can be accepted upon mouse drop.
*/
void setAcceptDragOperation(bool canAccept) override;
@ -401,7 +401,7 @@ class GHOST_Window : public GHOST_IWindow {
/** The presence of progress indicator with the application icon */
bool m_progressBarVisible;
/** The acceptance of the "drop candidate" of the current drag'n'drop operation */
/** The acceptance of the "drop candidate" of the current drag & drop operation. */
bool m_canAcceptDragOperation;
/** Modified state : are there unsaved changes */

View File

@ -172,7 +172,7 @@
return (associatedWindow->isDialog() || !systemCocoa->hasDialogWindow());
}
/* The drag'n'drop dragging destination methods. */
/* The drag & drop dragging destination methods. */
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
NSPoint mouseLocation = [sender draggingLocation];

View File

@ -44,7 +44,7 @@
const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
const int GHOST_WindowWin32::s_maxTitleLength = 128;
/* force NVidia Optimus to used dedicated graphics */
/* force NVidia OPTIMUS to used dedicated graphics */
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}

View File

@ -465,7 +465,7 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
# if 0
/* Ideally we'd just create a render target view for the OpenXR swap-chain image texture and
* blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
* this though. At least not with Optimus hardware. See:
* this though. At least not with OPTIMUS hardware. See:
* https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
*/

View File

@ -147,7 +147,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator):
# Don't set the constraint axis since users will expect MMB
# to use the user setting, see: #61637
# "orient_type": 'NORMAL',
# Not a popular choice, too restrictive for retopo.
# Not a popular choice, too restrictive for retopology.
# "constraint_axis": (True, True, False),
"constraint_axis": (False, False, False),
"release_confirm": False,

View File

@ -904,6 +904,8 @@ class VIEW3D_HT_header(Header):
if object_mode == 'PAINT_GPENCIL':
row = layout.row(align=True)
sub.prop(tool_settings, "use_gpencil_draw_onback", text="", icon='MOD_OPACITY')
sub.separator(factor=0.4)
row.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:

View File

@ -143,9 +143,6 @@ struct SpaceType {
/* region type definitions */
ListBase regiontypes;
/** Asset shelf type definitions. */
blender::Vector<std::unique_ptr<AssetShelfType>> asset_shelf_types;
/* read and write... */
/** Default key-maps to add. */
@ -534,6 +531,9 @@ struct AssetShelfType {
int space_type;
/** Operator to call when activating a grid view item. */
std::string activate_operator;
AssetShelfTypeFlag flag;
short default_preview_size;
@ -552,6 +552,8 @@ struct AssetShelfType {
const blender::asset_system::AssetRepresentation *asset,
uiLayout *layout);
const AssetWeakReference *(*get_active_asset)(const AssetShelfType *shelf_type);
/* RNA integration */
ExtensionRNA rna_ext;
};

View File

@ -1380,8 +1380,7 @@ static bool start_ffmpeg_impl(FFMpegContext *context,
break;
default:
/* These containers are not restricted to any specific codec types.
* Currently we expect these to be .avi, .mov, .mkv, and .mp4.
*/
* Currently we expect these to be `.avi`, `.mov`, `.mkv`, and `.mp4`. */
video_codec = context->ffmpeg_codec;
break;
}

View File

@ -1905,7 +1905,7 @@ void add_edge_constraint(
* Fill crossings array with CrossData points for intersection path from v1 to v2.
*
* At every point, the crossings array has the path so far, except that
* the .out field of the last element of it may not be known yet -- if that
* the `.out` field of the last element of it may not be known yet -- if that
* last element is a vertex, then we won't know the output edge until we
* find the next crossing.
*

View File

@ -306,7 +306,7 @@ void rgb_to_hsv_compat(float r, float g, float b, float *r_h, float *r_s, float
rgb_to_hsv(r, g, b, r_h, r_s, r_v);
if (*r_v <= 1e-8) {
/* Very low v values will affect the hs values, correct them in post. */
/* Very low V values will affect the HS values, correct them in post. */
*r_h = orig_h;
*r_s = orig_s;
}

View File

@ -615,7 +615,7 @@ class CellsInfo {
};
/**
* For Debugging: write a .obj file showing the patch/cell structure or just the cells.
* For Debugging: write an `.obj` file showing the patch/cell structure or just the cells.
*/
static void write_obj_cell_patch(const IMesh &m,
const CellsInfo &cinfo,

View File

@ -284,7 +284,7 @@ size_t BLI_string_flip_side_name(char *name_dst,
/* always copy the name, since this can be called with an uninitialized string */
len = BLI_strncpy_rlen(name_dst, name_src, name_dst_maxncpy);
if (len < 3) {
/* we don't do names like .R or .L */
/* We don't support names such as `.R` or `.L`. */
return len;
}

View File

@ -455,8 +455,8 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na
BLI_mutex_lock(&render->update_render_passes_mutex);
/* WORKAROUND: We use texture read to avoid using a frame-buffer to get the render result.
* However, on some implementation, we need a buffer with a few extra bytes for the read to
* happen correctly (see GLTexture::read()). So we need a custom memory allocation. */
/* Avoid memcpy(), replace the pointer directly. */
* happen correctly (see #GLTexture::read()). So we need a custom memory allocation. */
/* Avoid #memcpy(), replace the pointer directly. */
RE_pass_set_buffer_data(rp, result);
BLI_mutex_unlock(&render->update_render_passes_mutex);
}

View File

@ -28,7 +28,7 @@ static gpu::VertBuf *vbo_from_vector(Vector<Vertex> &vector)
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, vector.size());
Vertex *vbo_data = (Vertex *)GPU_vertbuf_get_data(vbo);
/* Copy data to VBO using a wrapper span. Could use memcpy if that's too slow. */
/* Copy data to VBO using a wrapper span. Could use #memcpy if that's too slow. */
MutableSpan<Vertex> span(vbo_data, vector.size());
span.copy_from(vector);
return vbo;

View File

@ -8,6 +8,8 @@
#pragma once
#include <memory>
struct ARegion;
struct ARegionType;
struct AssetShelf;
@ -22,6 +24,10 @@ struct SpaceType;
struct RegionPollParams;
struct wmWindowManager;
namespace blender {
class StringRef;
} // namespace blender
namespace blender::ed::asset::shelf {
/* -------------------------------------------------------------------- */
@ -64,7 +70,15 @@ void header_regiontype_register(ARegionType *region_type, const int space_type);
/** \name Asset Shelf Type
* \{ */
bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type);
void type_register(std::unique_ptr<AssetShelfType> type);
void type_unregister(const AssetShelfType &shelf_type);
/**
* Poll an asset shelf type for display as a permanent region in a space of a given type (the
* type's #bl_space_type).
*/
bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type);
AssetShelfType *type_find_from_idname(const StringRef idname);
/** \} */

View File

@ -51,30 +51,70 @@ void send_redraw_notifier(const bContext &C)
/** \name Shelf Type
* \{ */
bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type)
static Vector<std::unique_ptr<AssetShelfType>> &static_shelf_types()
{
static Vector<std::unique_ptr<AssetShelfType>> shelf_types;
return shelf_types;
}
void type_register(std::unique_ptr<AssetShelfType> type)
{
Vector<std::unique_ptr<AssetShelfType>> &shelf_types = static_shelf_types();
shelf_types.append(std::move(type));
}
void type_unregister(const AssetShelfType &shelf_type)
{
Vector<std::unique_ptr<AssetShelfType>> &shelf_types = static_shelf_types();
auto *const it = std::find_if(shelf_types.begin(),
shelf_types.end(),
[&](const std::unique_ptr<AssetShelfType> &iter_type) {
return iter_type.get() == &shelf_type;
});
BLI_assert(it != shelf_types.end());
shelf_types.remove(it - shelf_types.begin());
}
bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type)
{
if (!shelf_type) {
return false;
}
if (shelf_type->space_type && (space_type != shelf_type->space_type)) {
return false;
}
BLI_assert_msg(std::find_if(space_type.asset_shelf_types.begin(),
space_type.asset_shelf_types.end(),
#ifndef NDEBUG
const Vector<std::unique_ptr<AssetShelfType>> &shelf_types = static_shelf_types();
BLI_assert_msg(std::find_if(shelf_types.begin(),
shelf_types.end(),
[&](const std::unique_ptr<AssetShelfType> &type) {
return type.get() == shelf_type;
}) != space_type.asset_shelf_types.end(),
}) != shelf_types.end(),
"Asset shelf type is not registered");
UNUSED_VARS_NDEBUG(space_type);
#endif
return !shelf_type->poll || shelf_type->poll(&C, shelf_type);
}
AssetShelfType *type_ensure(const SpaceType &space_type, AssetShelf &shelf)
AssetShelfType *type_find_from_idname(const StringRef idname)
{
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (idname == shelf_type->idname) {
return shelf_type.get();
}
}
return nullptr;
}
AssetShelfType *ensure_shelf_has_type(AssetShelf &shelf)
{
if (shelf.type) {
return shelf.type;
}
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (STREQ(shelf.idname, shelf_type->idname)) {
shelf.type = shelf_type.get();
return shelf_type.get();
@ -138,7 +178,7 @@ static void activate_shelf(RegionAssetShelf &shelf_regiondata, AssetShelf &shelf
* current context (all polls failed).
*/
static AssetShelf *update_active_shelf(const bContext &C,
const SpaceType &space_type,
const eSpace_Type space_type,
RegionAssetShelf &shelf_regiondata,
FunctionRef<void(AssetShelf &new_shelf)> on_create)
{
@ -146,7 +186,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
/* Case 1: */
if (shelf_regiondata.active_shelf &&
type_poll(C, space_type, type_ensure(space_type, *shelf_regiondata.active_shelf)))
type_poll(C, ensure_shelf_has_type(*shelf_regiondata.active_shelf), space_type))
{
/* Not a strong precondition, but if this is wrong something weird might be going on. */
BLI_assert(shelf_regiondata.active_shelf == shelf_regiondata.shelves.first);
@ -160,7 +200,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
continue;
}
if (type_poll(C, space_type, type_ensure(space_type, *shelf))) {
if (type_poll(C, ensure_shelf_has_type(*shelf), space_type)) {
/* Found a valid previously activated shelf, reactivate it. */
activate_shelf(shelf_regiondata, *shelf);
return shelf;
@ -168,8 +208,8 @@ static AssetShelf *update_active_shelf(const bContext &C,
}
/* Case 3: */
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
if (type_poll(C, space_type, shelf_type.get())) {
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (type_poll(C, shelf_type.get(), space_type)) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
BLI_addhead(&shelf_regiondata.shelves, new_shelf);
/* Moves ownership to the regiondata. */
@ -216,11 +256,9 @@ void region_free(ARegion *region)
*/
static bool asset_shelf_space_poll(const bContext *C, const SpaceLink *space_link)
{
const SpaceType *space_type = BKE_spacetype_from_id(space_link->spacetype);
/* Is there any asset shelf type registered that returns true for it's poll? */
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type->asset_shelf_types) {
if (type_poll(*C, *space_type, shelf_type.get())) {
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (type_poll(*C, shelf_type.get(), space_link->spacetype)) {
return true;
}
}
@ -251,6 +289,9 @@ static void asset_shelf_region_listen(const wmRegionListenerParams *params)
ED_region_tag_redraw(region);
}
break;
case NC_ASSET:
ED_region_tag_redraw(region);
break;
}
}
@ -484,7 +525,10 @@ void region_on_poll_success(const bContext *C, ARegion *region)
ScrArea *area = CTX_wm_area(C);
update_active_shelf(
*C, *area->type, *shelf_regiondata, /*on_create=*/[&](AssetShelf &new_shelf) {
*C,
eSpace_Type(area->spacetype),
*shelf_regiondata,
/*on_create=*/[&](AssetShelf &new_shelf) {
/* Update region visibility (`'DEFAULT_VISIBLE'` option). */
const int old_flag = region->flag;
SET_FLAG_FROM_TEST(region->flag,
@ -553,7 +597,6 @@ AssetShelf *active_shelf_from_area(const ScrArea *area)
const ARegion *shelf_region = BKE_area_find_region_type(area, RGN_TYPE_ASSET_SHELF);
if (!shelf_region) {
/* Called in wrong context, area doesn't have a shelf. */
BLI_assert_unreachable();
return nullptr;
}
@ -613,7 +656,7 @@ int context(const bContext *C, const char *member, bContextDataResult *result)
/* XXX hack. Get the asset from the active item, but needs to be the file... */
if (CTX_data_equals(member, "active_file")) {
const ARegion *region = CTX_wm_region(C);
const uiBut *but = UI_region_views_find_active_item_but(region);
const uiBut *but = UI_region_views_find_mouse_over_but(CTX_wm_window(C), region);
if (!but) {
return CTX_RESULT_NO_DATA;
}

View File

@ -43,7 +43,7 @@ AssetShelf *active_shelf_from_context(const bContext *C);
void send_redraw_notifier(const bContext &C);
AssetShelfType *type_ensure(const SpaceType &space_type, AssetShelf &shelf);
AssetShelfType *ensure_shelf_has_type(AssetShelf &shelf);
AssetShelf *create_shelf_from_type(AssetShelfType &type);
/**

View File

@ -21,6 +21,7 @@
#include "ED_asset_handle.hh"
#include "ED_asset_list.hh"
#include "ED_asset_menu_utils.hh"
#include "ED_asset_shelf.hh"
#include "UI_grid_view.hh"
@ -38,6 +39,7 @@ namespace blender::ed::asset::shelf {
class AssetView : public ui::AbstractGridView {
const AssetLibraryReference library_ref_;
const AssetShelf &shelf_;
std::optional<AssetWeakReference> active_asset_;
/** Copy of the filter string from #AssetShelfSettings, with extra '*' added to the beginning and
* end of the string, for `fnmatch()` to work. */
char search_string[sizeof(AssetShelfSettings::search_string) + 2] = "";
@ -68,6 +70,7 @@ class AssetViewItem : public ui::PreviewGridItem {
void disable_asset_drag();
void build_grid_tile(uiLayout &layout) const override;
void build_context_menu(bContext &C, uiLayout &column) const override;
std::optional<bool> should_be_active() const override;
bool is_filtered_visible() const override;
std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
@ -90,6 +93,14 @@ AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf
BLI_strncpy_ensure_pad(
search_string, shelf.settings.search_string, '*', sizeof(search_string));
}
if (shelf.type->get_active_asset) {
if (const AssetWeakReference *weak_ref = shelf.type->get_active_asset(shelf.type)) {
active_asset_ = *weak_ref;
}
else {
active_asset_.reset();
}
}
}
void AssetView::build_items()
@ -192,16 +203,17 @@ void AssetViewItem::disable_asset_drag()
void AssetViewItem::build_grid_tile(uiLayout &layout) const
{
PointerRNA file_ptr = RNA_pointer_create(
nullptr,
&RNA_FileSelectEntry,
/* XXX passing file pointer here, should be asset handle or asset representation. */
const_cast<FileDirEntry *>(asset_.file_data));
const AssetView &asset_view = reinterpret_cast<const AssetView &>(this->get_view());
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
uiBlock *block = uiLayoutGetBlock(&layout);
UI_but_context_ptr_set(
block, reinterpret_cast<uiBut *>(view_item_but_), "active_file", &file_ptr);
ui::PreviewGridItem::build_grid_tile(layout);
wmOperatorType *ot = WM_operatortype_find(shelf_type.activate_operator.c_str(), true);
PointerRNA op_props = PointerRNA_NULL;
if (ot) {
WM_operator_properties_create_ptr(&op_props, ot);
asset::operator_asset_reference_props_set(*handle_get_representation(&asset_), op_props);
}
ui::PreviewGridItem::build_grid_tile_button(layout, ot, &op_props);
}
void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
@ -214,6 +226,19 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
}
}
std::optional<bool> AssetViewItem::should_be_active() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
if (!asset_view.active_asset_) {
return false;
}
const asset_system::AssetRepresentation *asset = handle_get_representation(&asset_);
AssetWeakReference weak_ref = asset->make_weak_reference();
const bool matches = *asset_view.active_asset_ == weak_ref;
return matches;
}
bool AssetViewItem::is_filtered_visible() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());

View File

@ -72,6 +72,12 @@ void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob)
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
nmd.node_group = ntreeAddTree(bmain, DATA_("Surface Deform"), "GeometryNodeTree");
if (!nmd.node_group->geometry_node_asset_traits) {
nmd.node_group->geometry_node_asset_traits = MEM_new<GeometryNodeAssetTraits>(__func__);
}
nmd.node_group->geometry_node_asset_traits->flag |= GEO_NODE_ASSET_MODIFIER;
bNodeTree *ntree = nmd.node_group;
ntree->tree_interface.add_socket(
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);

View File

@ -1291,22 +1291,20 @@ void ui_template_node_operator_asset_root_items(uiLayout &layout, const bContext
*tree = build_catalog_tree(C, *active_object);
}
asset_system::AssetLibrary *all_library = asset::list::library_get_once_available(
asset_system::all_library_reference());
if (!all_library) {
return;
if (asset_system::AssetLibrary *all_library = asset::list::library_get_once_available(
asset_system::all_library_reference()))
{
const Set<std::string> builtin_menus = get_builtin_menus(ObjectType(active_object->type),
eObjectMode(active_object->mode));
tree->catalogs.foreach_root_item([&](const asset_system::AssetCatalogTreeItem &item) {
if (!builtin_menus.contains_as(item.catalog_path().str())) {
asset::draw_menu_for_catalog(
screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", layout);
}
});
}
const Set<std::string> builtin_menus = get_builtin_menus(ObjectType(active_object->type),
eObjectMode(active_object->mode));
tree->catalogs.foreach_root_item([&](const asset_system::AssetCatalogTreeItem &item) {
if (!builtin_menus.contains_as(item.catalog_path().str())) {
asset::draw_menu_for_catalog(
screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", layout);
}
});
if (!tree->unassigned_assets.is_empty() || unassigned_local_poll(C)) {
uiItemM(&layout, "GEO_MT_node_operator_unassigned", "", ICON_FILE_HIDDEN);
}

View File

@ -19,6 +19,7 @@
#include "UI_resources.hh"
struct bContext;
struct PointerRNA;
struct uiBlock;
struct uiButViewItem;
struct uiLayout;
@ -204,6 +205,13 @@ class PreviewGridItem : public AbstractGridViewItem {
void build_grid_tile(uiLayout &layout) const override;
/**
* \note Takes ownership of the operator properties defined in \a op_props.
*/
void build_grid_tile_button(uiLayout &layout,
const wmOperatorType *ot = nullptr,
const PointerRNA *op_props = nullptr) const;
/**
* Set a custom callback to execute when activating this view item. This way users don't have to
* sub-class #PreviewGridItem, just to implement custom activation behavior (a common thing to

View File

@ -3394,4 +3394,4 @@ blender::ui::AbstractView *UI_region_view_find_at(const ARegion *region, const i
blender::ui::AbstractViewItem *UI_region_views_find_item_at(const ARegion &region,
const int xy[2]);
blender::ui::AbstractViewItem *UI_region_views_find_active_item(const ARegion *region);
uiBut *UI_region_views_find_active_item_but(const ARegion *region);
uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region);

View File

@ -9,6 +9,7 @@
#include <cfloat>
#include <cmath>
#include <limits>
#include <optional>
#include <stdexcept>
#include "BKE_icons.h"
@ -17,6 +18,8 @@
#include "WM_types.hh"
#include "RNA_access.hh"
#include "UI_interface.hh"
#include "interface_intern.hh"
@ -405,23 +408,42 @@ PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int prev
{
}
void PreviewGridItem::build_grid_tile(uiLayout &layout) const
void PreviewGridItem::build_grid_tile_button(uiLayout &layout,
const wmOperatorType *ot,
const PointerRNA *op_props) const
{
const GridViewStyle &style = this->get_view().get_style();
uiBlock *block = uiLayoutGetBlock(&layout);
uiBut *but = uiDefBut(block,
uiBut *but;
if (ot) {
but = uiDefButO_ptr(block,
UI_BTYPE_PREVIEW_TILE,
0,
const_cast<wmOperatorType *>(ot),
WM_OP_INVOKE_REGION_WIN,
hide_label_ ? "" : label,
0,
0,
style.tile_width,
style.tile_height,
nullptr,
0,
0,
"");
but->opptr = MEM_new<PointerRNA>(__func__, *op_props);
}
else {
but = uiDefBut(block,
UI_BTYPE_PREVIEW_TILE,
0,
hide_label_ ? "" : label,
0,
0,
style.tile_width,
style.tile_height,
nullptr,
0,
0,
"");
}
/* Draw icons that are not previews or images as normal icons with a fixed icon size. Otherwise
* they will be upscaled to the button size. Should probably be done by the widget code. */
const int is_preview_flag = (BKE_icon_is_preview(preview_icon_id) ||
@ -436,6 +458,11 @@ void PreviewGridItem::build_grid_tile(uiLayout &layout) const
but->emboss = UI_EMBOSS_NONE;
}
void PreviewGridItem::build_grid_tile(uiLayout &layout) const
{
this->build_grid_tile_button(layout);
}
void PreviewGridItem::set_on_activate_fn(ActivateFn fn)
{
activate_fn_ = fn;

View File

@ -201,9 +201,9 @@ ui::AbstractViewItem *UI_region_views_find_active_item(const ARegion *region)
return item_but->view_item;
}
uiBut *UI_region_views_find_active_item_but(const ARegion *region)
uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region)
{
return ui_view_item_find_active(region);
return ui_view_item_find_mouse_over(region, win->eventstate->xy);
}
namespace blender::ui {

View File

@ -697,11 +697,13 @@ void WM_OT_alembic_import(wmOperatorType *ot)
"Set Frame Range",
"If checked, update scene's start and end frame to match those of the Alembic archive");
RNA_def_boolean(ot->srna,
"validate_meshes",
false,
"Validate Meshes",
"Check imported mesh objects for invalid data (slow)");
RNA_def_boolean(
ot->srna,
"validate_meshes",
false,
"Validate Meshes",
"Ensure the data is valid "
"(when disabled, data may be imported which causes crashes displaying or editing)");
RNA_def_boolean(ot->srna,
"always_add_cache_reader",

View File

@ -392,7 +392,7 @@ void WM_OT_obj_export(wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "smooth_group_bitflags", false, "Generate Bitflags for Smooth Groups", "");
/* Only show .obj or .mtl files by default. */
/* Only show `.obj` or `.mtl` files by default. */
prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
@ -537,11 +537,13 @@ void WM_OT_obj_import(wmOperatorType *ot)
false,
"Vertex Groups",
"Import OBJ groups as vertex groups");
RNA_def_boolean(ot->srna,
"validate_meshes",
false,
"Validate Meshes",
"Check imported mesh objects for invalid data (slow)");
RNA_def_boolean(
ot->srna,
"validate_meshes",
true,
"Validate Meshes",
"Ensure the data is valid "
"(when disabled, data may be imported which causes crashes displaying or editing)");
RNA_def_string(ot->srna,
"collection_separator",
@ -550,7 +552,7 @@ void WM_OT_obj_import(wmOperatorType *ot)
"Path Separator",
"Character used to separate objects name into hierarchical structure");
/* Only show .obj or .mtl files by default. */
/* Only show `.obj` or `.mtl` files by default. */
prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
}

View File

@ -238,7 +238,7 @@ void WM_OT_ply_export(wmOperatorType *ot)
"ASCII Format",
"Export file in ASCII format, export as binary otherwise");
/* Only show .ply files by default. */
/* Only show `.ply` files by default. */
prop = RNA_def_string(ot->srna, "filter_glob", "*.ply", 0, "Extension Filter", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
@ -339,7 +339,7 @@ void WM_OT_ply_import(wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "import_attributes", true, "Vertex Attributes", "Import custom vertex attributes");
/* Only show .ply files by default. */
/* Only show `.ply` files by default. */
prop = RNA_def_string(ot->srna, "filter_glob", "*.ply", 0, "Extension Filter", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
}

View File

@ -184,7 +184,7 @@ void WM_OT_stl_export(wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "apply_modifiers", true, "Apply Modifiers", "Apply modifiers to exported meshes");
/* Only show .stl files by default. */
/* Only show `.stl` files by default. */
prop = RNA_def_string(ot->srna, "filter_glob", "*.stl", 0, "Extension Filter", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
@ -291,13 +291,16 @@ void WM_OT_stl_import(wmOperatorType *ot)
"Use (import) facet normals (note that this will still give flat shading)");
RNA_def_enum(ot->srna, "forward_axis", io_transform_axis, IO_AXIS_Y, "Forward Axis", "");
RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Z, "Up Axis", "");
RNA_def_boolean(ot->srna,
"use_mesh_validate",
false,
"Validate Mesh",
"Validate and correct imported mesh (slow)");
/* Only show .stl files by default. */
RNA_def_boolean(
ot->srna,
"use_mesh_validate",
true,
"Validate Mesh",
"Ensure the data is valid "
"(when disabled, data may be imported which causes crashes displaying or editing)");
/* Only show `.stl` files by default. */
prop = RNA_def_string(ot->srna, "filter_glob", "*.stl", 0, "Extension Filter", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
}

View File

@ -884,11 +884,13 @@ void WM_OT_usd_import(wmOperatorType *ot)
"Custom Properties",
"Behavior when importing USD attributes as Blender custom properties");
RNA_def_boolean(ot->srna,
"validate_meshes",
false,
"Validate Meshes",
"Check imported mesh objects for invalid data (slow)");
RNA_def_boolean(
ot->srna,
"validate_meshes",
false,
"Validate Meshes",
"Ensure the data is valid "
"(when disabled, data may be imported which causes crashes displaying or editing)");
}
namespace blender::ed::io {

View File

@ -137,7 +137,7 @@ class PaintOperation : public GreasePencilStrokeOperation {
void on_stroke_done(const bContext &C) override;
private:
void simplify_stroke(bke::greasepencil::Drawing &drawing, float epsilon_px);
void simplify_stroke(const bContext &C, bke::greasepencil::Drawing &drawing, float epsilon_px);
void process_stroke_end(const bContext &C, bke::greasepencil::Drawing &drawing);
};
@ -190,6 +190,100 @@ struct PaintOperationExecutor {
BLI_assert(drawing_ != nullptr);
}
/**
* Creates a new curve with one point at the beginning or end.
* Note: Does not initialize the new curve or points.
*/
static void create_blank_curve(bke::CurvesGeometry &curves, const bool on_back)
{
if (!on_back) {
const int num_old_points = curves.points_num();
curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
curves.offsets_for_write().last(1) = num_old_points;
return;
}
curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
MutableSpan<int> offsets = curves.offsets_for_write();
offsets.first() = 0;
/* Loop through backwards to not overwrite the data. */
for (int i = curves.curves_num() - 2; i >= 0; i--) {
offsets[i + 1] = offsets[i] + 1;
}
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
bke::GSpanAttributeWriter dst = attributes.lookup_for_write_span(id);
GMutableSpan attribute_data = dst.span;
bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> span_data = attribute_data.typed<T>();
/* Loop through backwards to not overwrite the data. */
for (int i = span_data.size() - 2; i >= 0; i--) {
span_data[i + 1] = span_data[i];
}
});
dst.finish();
return true;
});
}
/**
* Extends the first or last curve by `new_points_num` number of points.
* Note: Does not initialize the new points.
*/
static void extend_curve(bke::CurvesGeometry &curves,
const bool on_back,
const int new_points_num)
{
if (!on_back) {
curves.resize(curves.points_num() + new_points_num, curves.curves_num());
curves.offsets_for_write().last() = curves.points_num();
return;
}
const int last_active_point = curves.points_by_curve()[0].last();
curves.resize(curves.points_num() + new_points_num, curves.curves_num());
MutableSpan<int> offsets = curves.offsets_for_write();
for (const int src_curve : curves.curves_range().drop_front(1)) {
offsets[src_curve] = offsets[src_curve] + new_points_num;
}
offsets.last() = curves.points_num();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
attributes.for_all([&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
if (meta_data.domain != bke::AttrDomain::Point) {
return true;
}
bke::GSpanAttributeWriter dst = attributes.lookup_for_write_span(id);
GMutableSpan attribute_data = dst.span;
bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> span_data = attribute_data.typed<T>();
/* Loop through backwards to not overwrite the data. */
for (int i = (span_data.size() - 1) - new_points_num; i >= last_active_point; i--) {
span_data[i + new_points_num] = span_data[i];
}
});
dst.finish();
return true;
});
curves.tag_topology_changed();
}
/* Attributes that are defined explicitly and should not be copied from original geometry. */
Set<std::string> skipped_attribute_ids(const bke::AttrDomain domain) const
{
@ -237,6 +331,8 @@ struct PaintOperationExecutor {
settings_);
const float start_opacity = ed::greasepencil::opacity_from_input_sample(
start_sample.pressure, brush_, scene_, settings_);
Scene *scene = CTX_data_scene(&C);
const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
self.screen_space_coords_orig_.append(start_coords);
self.screen_space_curve_fitted_coords_.append(Vector<float2>({start_coords}));
@ -244,18 +340,21 @@ struct PaintOperationExecutor {
/* Resize the curves geometry so there is one more curve with a single point. */
bke::CurvesGeometry &curves = drawing_->strokes_for_write();
const int num_old_points = curves.points_num();
curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
curves.offsets_for_write().last(1) = num_old_points;
create_blank_curve(curves, on_back);
curves.positions_for_write().last() = self.placement_.project(start_coords);
drawing_->radii_for_write().last() = start_radius;
drawing_->opacities_for_write().last() = start_opacity;
const int active_curve = on_back ? curves.curves_range().first() :
curves.curves_range().last();
const IndexRange curve_points = curves.points_by_curve()[active_curve];
const int last_active_point = curve_points.last();
curves.positions_for_write()[last_active_point] = self.placement_.project(start_coords);
drawing_->radii_for_write()[last_active_point] = start_radius;
drawing_->opacities_for_write()[last_active_point] = start_opacity;
if (vertex_color_) {
drawing_->vertex_colors_for_write().last() = *vertex_color_;
drawing_->vertex_colors_for_write()[last_active_point] = *vertex_color_;
}
if (fill_color_) {
drawing_->fill_colors_for_write().last() = *fill_color_;
drawing_->fill_colors_for_write()[active_curve] = *fill_color_;
}
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
@ -267,22 +366,22 @@ struct PaintOperationExecutor {
"hardness",
bke::AttrDomain::Curve,
bke::AttributeInitVArray(VArray<float>::ForSingle(1.0f, curves.curves_num())));
cyclic.span.last() = false;
materials.span.last() = material_index;
hardnesses.span.last() = hardness_;
cyclic.span[active_curve] = false;
materials.span[active_curve] = material_index;
hardnesses.span[active_curve] = hardness_;
/* Only set the attribute if the type is not the default or if it already exists. */
if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
bke::SpanAttributeWriter<int8_t> start_caps =
attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve);
start_caps.span.last() = settings_->caps_type;
start_caps.span[active_curve] = settings_->caps_type;
start_caps.finish();
}
if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
bke::SpanAttributeWriter<int8_t> end_caps = attributes.lookup_or_add_for_write_span<int8_t>(
"end_cap", bke::AttrDomain::Curve);
end_caps.span.last() = settings_->caps_type;
end_caps.span[active_curve] = settings_->caps_type;
end_caps.finish();
}
@ -290,18 +389,18 @@ struct PaintOperationExecutor {
materials.finish();
hardnesses.finish();
curves.curve_types_for_write().last() = CURVE_TYPE_POLY;
curves.curve_types_for_write()[active_curve] = CURVE_TYPE_POLY;
curves.update_curve_types();
/* Initialize the rest of the attributes with default values. */
bke::fill_attribute_range_default(attributes,
bke::AttrDomain::Point,
this->skipped_attribute_ids(bke::AttrDomain::Point),
curves.points_range().take_back(1));
IndexRange(last_active_point, 1));
bke::fill_attribute_range_default(attributes,
bke::AttrDomain::Curve,
this->skipped_attribute_ids(bke::AttrDomain::Curve),
curves.curves_range().take_back(1));
IndexRange(active_curve, 1));
drawing_->tag_topology_changed();
}
@ -405,14 +504,21 @@ struct PaintOperationExecutor {
settings_);
const float opacity = ed::greasepencil::opacity_from_input_sample(
extension_sample.pressure, brush_, scene_, settings_);
Scene *scene = CTX_data_scene(&C);
const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
bke::CurvesGeometry &curves = drawing_->strokes_for_write();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const int active_curve = on_back ? curves.curves_range().first() :
curves.curves_range().last();
const IndexRange curve_points = curves.points_by_curve()[active_curve];
const int last_active_point = curve_points.last();
const float2 prev_coords = self.screen_space_coords_orig_.last();
const float prev_radius = drawing_->radii().last();
const float prev_opacity = drawing_->opacities().last();
const ColorGeometry4f prev_vertex_color = drawing_->vertex_colors().last();
const float prev_radius = drawing_->radii()[last_active_point];
const float prev_opacity = drawing_->opacities()[last_active_point];
const ColorGeometry4f prev_vertex_color = drawing_->vertex_colors()[last_active_point];
/* Overwrite last point if it's very close. */
const IndexRange points_range = curves.points_by_curve()[curves.curves_range().last()];
@ -420,10 +526,10 @@ struct PaintOperationExecutor {
if (math::distance(coords, prev_coords) < POINT_OVERRIDE_THRESHOLD_PX) {
/* Don't move the first point of the stroke. */
if (!is_first_sample) {
curves.positions_for_write().last() = self.placement_.project(coords);
curves.positions_for_write()[last_active_point] = self.placement_.project(coords);
}
drawing_->radii_for_write().last() = math::max(radius, prev_radius);
drawing_->opacities_for_write().last() = math::max(opacity, prev_opacity);
drawing_->radii_for_write()[last_active_point] = math::max(radius, prev_radius);
drawing_->opacities_for_write()[last_active_point] = math::max(opacity, prev_opacity);
return;
}
@ -436,12 +542,9 @@ struct PaintOperationExecutor {
}
/* Resize the curves geometry. */
curves.resize(curves.points_num() + new_points_num, curves.curves_num());
curves.offsets_for_write().last() = curves.points_num();
const IndexRange curve_points = curves.points_by_curve()[curves.curves_range().last()];
extend_curve(curves, on_back, new_points_num);
/* Subdivide stroke in new_points. */
const IndexRange new_points = curve_points.take_back(new_points_num);
const IndexRange new_points = curves.points_by_curve()[active_curve].take_back(new_points_num);
Array<float2> new_screen_space_coords(new_points_num);
MutableSpan<float3> positions = curves.positions_for_write();
MutableSpan<float3> new_positions = positions.slice(new_points);
@ -473,7 +576,8 @@ struct PaintOperationExecutor {
}
else {
/* Active smoothing is done in a window at the end of the new stroke. */
this->active_smoothing(self, smooth_window, positions.slice(curve_points));
this->active_smoothing(
self, smooth_window, positions.slice(curves.points_by_curve()[active_curve]));
}
/* Initialize the rest of the attributes with default values. */
@ -483,7 +587,7 @@ struct PaintOperationExecutor {
curves.points_range().take_back(1));
drawing_->set_texture_matrices(Span<float4x2>(&(self.texture_space_), 1),
IndexMask(IndexRange(curves.curves_range().last(), 1)));
IndexMask(IndexRange(active_curve, 1)));
}
void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
@ -609,10 +713,15 @@ static float dist_to_interpolated_2d(
return 0.0f;
}
void PaintOperation::simplify_stroke(bke::greasepencil::Drawing &drawing, const float epsilon_px)
void PaintOperation::simplify_stroke(const bContext &C,
bke::greasepencil::Drawing &drawing,
const float epsilon_px)
{
const int stroke_index = drawing.strokes().curves_range().last();
const IndexRange points = drawing.strokes().points_by_curve()[stroke_index];
const Scene *scene = CTX_data_scene(&C);
const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
const int active_curve = on_back ? drawing.strokes().curves_range().first() :
drawing.strokes().curves_range().last();
const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const VArray<float> radii = drawing.radii();
@ -643,11 +752,60 @@ void PaintOperation::simplify_stroke(bke::greasepencil::Drawing &drawing, const
}
}
static void remove_points_from_end_of_active_curve(bke::CurvesGeometry &curves,
const bool on_back,
const int rem_points_num)
{
if (!on_back) {
curves.resize(curves.points_num() - rem_points_num, curves.curves_num());
curves.offsets_for_write().last() = curves.points_num();
return;
}
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const int last_active_point = curves.points_by_curve()[0].last();
/* Shift the data before resizing to not delete the data at the end. */
attributes.for_all([&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
if (meta_data.domain != bke::AttrDomain::Point) {
return true;
}
bke::GSpanAttributeWriter dst = attributes.lookup_for_write_span(id);
GMutableSpan attribute_data = dst.span;
bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> span_data = attribute_data.typed<T>();
for (int i = last_active_point - rem_points_num + 1;
i < curves.points_num() - rem_points_num;
i++)
{
span_data[i] = span_data[i + rem_points_num];
}
});
dst.finish();
return true;
});
curves.resize(curves.points_num() - rem_points_num, curves.curves_num());
MutableSpan<int> offsets = curves.offsets_for_write();
for (const int src_curve : curves.curves_range().drop_front(1)) {
offsets[src_curve] = offsets[src_curve] - rem_points_num;
}
offsets.last() = curves.points_num();
curves.tag_topology_changed();
}
void PaintOperation::process_stroke_end(const bContext &C, bke::greasepencil::Drawing &drawing)
{
Scene *scene = CTX_data_scene(&C);
const int stroke_index = drawing.strokes().curves_range().last();
IndexRange points = drawing.strokes().points_by_curve()[stroke_index];
const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
const int active_curve = on_back ? drawing.strokes().curves_range().first() :
drawing.strokes().curves_range().last();
IndexRange points = drawing.strokes().points_by_curve()[active_curve];
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const VArray<float> radii = drawing.radii();
@ -662,8 +820,7 @@ void PaintOperation::process_stroke_end(const bContext &C, bke::greasepencil::Dr
}
}
if (points_to_remove > 0) {
curves.resize(curves.points_num() - points_to_remove, curves.curves_num());
curves.offsets_for_write().last() = curves.points_num();
remove_points_from_end_of_active_curve(curves, on_back, points_to_remove);
points = points.drop_back(points_to_remove);
}
@ -674,7 +831,7 @@ void PaintOperation::process_stroke_end(const bContext &C, bke::greasepencil::Dr
curves, selection_domain, CD_PROP_BOOL);
if (selection_domain == bke::AttrDomain::Curve) {
ed::curves::fill_selection_false(selection.span.slice(IndexRange(stroke_index, 1)));
ed::curves::fill_selection_false(selection.span.slice(IndexRange(active_curve, 1)));
}
else if (selection_domain == bke::AttrDomain::Point) {
ed::curves::fill_selection_false(selection.span.slice(points));
@ -704,7 +861,7 @@ void PaintOperation::on_stroke_done(const bContext &C)
reinterpret_cast<GreasePencilDrawing *>(grease_pencil.drawing(drawing_index))->wrap();
const float simplifiy_threshold_px = 0.5f;
this->simplify_stroke(drawing, simplifiy_threshold_px);
this->simplify_stroke(C, drawing, simplifiy_threshold_px);
this->process_stroke_end(C, drawing);
drawing.tag_topology_changed();

View File

@ -2979,7 +2979,7 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op)
Sequence *seq, *seq_next;
Editing *ed = SEQ_editing_get(scene);
ListBase text_seq = {nullptr};
int iter = 1; /* Sequence numbers in .srt files are 1-indexed. */
int iter = 1; /* Sequence numbers in `.srt` files are 1-indexed. */
FILE *file;
char filepath[FILE_MAX];

View File

@ -68,7 +68,7 @@ class ConstantThicknessShader : public StrokeShader {
/* [ Thickness Shader ].
* Assigns an absolute constant external thickness to every vertices of the Stroke. The external
* thickness of a point is its thickness from the point to the strip border in the direction
* pointing outside the object the Stroke delimitates.
* pointing outside the object the Stroke delimiters.
*/
class ConstantExternThicknessShader : public StrokeShader {
public:

View File

@ -56,8 +56,8 @@ class MTLBackend : public GPUBackend {
void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override;
void compute_dispatch_indirect(StorageBuf *indirect_buf) override;
/* MTL Allocators need to be implemented in separate .mm files, due to allocation of Objective-C
* objects. */
/* MTL Allocators need to be implemented in separate `.mm` files,
* due to allocation of Objective-C objects. */
Context *context_alloc(void *ghost_window, void *ghost_context) override;
Batch *batch_alloc() override;
DrawList *drawlist_alloc(int list_length) override;

View File

@ -24,7 +24,7 @@ enum ePLYVertexColorMode {
};
struct PLYExportParams {
/** Full path to the destination .PLY file. */
/** Full path to the destination `.PLY` file. */
char filepath[FILE_MAX];
/** Pretend that destination file folder is this, if non-empty. Used only for tests. */
char file_base_for_tests[FILE_MAX];

View File

@ -99,16 +99,14 @@ Mesh *convert_ply_to_mesh(PlyData &data, const PLYImportParams &params)
uv_map.finish();
}
/* Calculate edges from the rest of the mesh. */
bke::mesh_calc_edges(*mesh, true, false);
/* If we have custom vertex normals, set them (note: important to do this
* after initializing the loops). */
bool set_custom_normals_for_verts = false;
if (!data.vertex_normals.is_empty()) {
if (!data.face_sizes.is_empty()) {
/* For a non-point-cloud mesh, set custom normals. */
BKE_mesh_set_custom_normals_from_verts(
mesh, reinterpret_cast<float(*)[3]>(data.vertex_normals.data()));
/* Deferred because this relies on valid mesh data. */
set_custom_normals_for_verts = true;
}
else if (params.import_attributes) {
/* If we have no faces, add vertex normals as custom attribute. */
@ -132,6 +130,23 @@ Mesh *convert_ply_to_mesh(PlyData &data, const PLYImportParams &params)
}
}
/* It's important to validate the mesh before using it's geometry to calculate derived data. */
{
/* Calculate edges from the rest of the mesh (this could be merged with validate). */
bke::mesh_calc_edges(*mesh, true, false);
bool verbose_validate = false;
#ifndef NDEBUG
verbose_validate = true;
#endif
BKE_mesh_validate(mesh, verbose_validate, false);
}
if (set_custom_normals_for_verts) {
BKE_mesh_set_custom_normals_from_verts(
mesh, reinterpret_cast<float(*)[3]>(data.vertex_normals.data()));
}
/* Merge all vertices on the same location. */
if (params.merge_verts) {
std::optional<Mesh *> merged_mesh = blender::geometry::mesh_merge_by_distance_all(

View File

@ -85,7 +85,7 @@ void export_frame(Depsgraph *depsgraph,
char filepath[FILE_MAX];
STRNCPY(filepath, export_params.filepath);
BLI_path_suffix(filepath, FILE_MAX, object_name, "");
/* Make sure we have .stl extension (case insensitive). */
/* Make sure we have `.stl` extension (case insensitive). */
if (!BLI_path_extension_check(filepath, ".stl")) {
BLI_path_extension_ensure(filepath, FILE_MAX, ".stl");
}

View File

@ -426,9 +426,11 @@ static void export_endjob(void *customdata)
report_job_duration(data);
}
/* To create a usdz file, we must first create a .usd/a/c file and then covert it to .usdz. The
* temporary files will be created in Blender's temporary session storage. The .usdz file will then
* be moved to job->usdz_filepath. */
/**
* To create a USDZ file, we must first create a `.usd/a/c` file and then covert it to `.usdz`.
* The temporary files will be created in Blender's temporary session storage.
* The `.usdz` file will then be moved to `job->usdz_filepath`.
*/
static void create_temp_path_for_usdz_export(const char *filepath,
blender::io::usd::ExportJobData *job)
{

View File

@ -95,8 +95,8 @@ std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volum
std::optional<std::string> vdb_file_path;
if (volume->filepath[0] == '\0') {
/* Entering this section should mean that Volume object contains OpenVDB data that is not
* obtained from external .vdb file but rather generated inside of Blender (i.e. by 'Mesh to
* Volume' modifier). Try to save this data to a .vdb file. */
* obtained from external `.vdb` file but rather generated inside of Blender (i.e. by 'Mesh to
* Volume' modifier). Try to save this data to a `.vdb` file. */
vdb_file_path = construct_vdb_file_path(volume);
if (!BKE_volume_save(

View File

@ -11,8 +11,8 @@ struct Volume;
namespace blender::io::usd {
/* Writer for writing OpenVDB assets to UsdVolVolume. Volume data is stored in separate .vdb files
* which are referenced in USD file. */
/* Writer for writing OpenVDB assets to UsdVolVolume. Volume data is stored in separate `.vdb`
* files which are referenced in USD file. */
class USDVolumeWriter : public USDAbstractWriter {
public:
USDVolumeWriter(const USDExporterContext &ctx);
@ -22,11 +22,11 @@ class USDVolumeWriter : public USDAbstractWriter {
virtual void do_write(HierarchyContext &context) override;
private:
/* Try to ensure that external .vdb file is available for USD to be referenced. Blender can
/* Try to ensure that external `.vdb` file is available for USD to be referenced. Blender can
* either reference external OpenVDB data or generate such data internally. Latter option will
* mean that `resolve_vdb_file` method will try to export volume data to a new .vdb file. If
* successful, this method returns absolute file path to the resolved .vdb file, if not, returns
* `std::nullopt`. */
* mean that `resolve_vdb_file` method will try to export volume data to a new `.vdb` file.
* If successful, this method returns absolute file path to the resolved `.vdb` file, if not,
* returns `std::nullopt`. */
std::optional<std::string> resolve_vdb_file(const Volume *volume) const;
std::optional<std::string> construct_vdb_file_path(const Volume *volume) const;

View File

@ -19,7 +19,7 @@ struct bContext;
struct ReportList;
struct OBJExportParams {
/** Full path to the destination .OBJ file. */
/** Full path to the destination `.OBJ` file. */
char filepath[FILE_MAX];
/** Pretend that destination file folder is this, if non-empty. Used only for tests. */
char file_base_for_tests[FILE_MAX];
@ -77,7 +77,7 @@ struct OBJImportParams {
bool use_split_objects = true;
bool use_split_groups = false;
bool import_vertex_groups = false;
bool validate_meshes = false;
bool validate_meshes = true;
bool relative_paths = true;
bool clear_selection = true;

View File

@ -173,7 +173,7 @@ void OBJWriter::write_header() const
void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
{
/* Split .MTL file path into parent directory and filename. */
/* Split `.MTL` file path into parent directory and filename. */
char mtl_file_name[FILE_MAXFILE];
char mtl_dir_name[FILE_MAXDIR];
BLI_path_split_dir_file(mtl_filepath.data(),
@ -497,7 +497,7 @@ void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_d
}
/* -------------------------------------------------------------------- */
/** \name .MTL writers.
/** \name `.MTL` writers.
* \{ */
static const char *tex_map_type_to_string[] = {

View File

@ -33,7 +33,7 @@ struct IndexOffsets {
};
/**
* Responsible for writing a .OBJ file.
* Responsible for writing a `.OBJ` file.
*/
class OBJWriter : NonMovable, NonCopyable {
private:
@ -70,7 +70,7 @@ class OBJWriter : NonMovable, NonCopyable {
*/
void write_object_name(FormatHandler &fh, const OBJMesh &obj_mesh_data) const;
/**
* Write file name of Material Library in .OBJ file.
* Write file name of Material Library in `.OBJ` file.
*/
void write_mtllib_name(const StringRefNull mtl_filepath) const;
/**
@ -93,7 +93,7 @@ class OBJWriter : NonMovable, NonCopyable {
* Write face elements with at least vertex indices, and conditionally with UV vertex
* indices and face normal indices. Also write groups: smooth, vertex, material.
* The matname_fn turns a 0-indexed material slot number in an Object into the
* name used in the .obj file.
* name used in the `.obj` file.
* \note UV indices were stored while writing UV vertices.
*/
void write_face_elements(FormatHandler &fh,
@ -107,7 +107,7 @@ class OBJWriter : NonMovable, NonCopyable {
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data) const;
/**
* Write a NURBS curve to the .OBJ file in parameter form.
* Write a NURBS curve to the `.OBJ` file in parameter form.
*/
void write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_data) const;
@ -162,7 +162,7 @@ class OBJWriter : NonMovable, NonCopyable {
};
/**
* Responsible for writing a .MTL file.
* Responsible for writing a `.MTL` file.
*/
class MTLWriter : NonMovable, NonCopyable {
private:
@ -175,7 +175,7 @@ class MTLWriter : NonMovable, NonCopyable {
public:
/*
* Create the .MTL file.
* Create the `.MTL` file.
*/
MTLWriter(const char *obj_filepath) noexcept(false);
~MTLWriter();

View File

@ -142,7 +142,7 @@ static std::string get_image_filepath(const bNode *tex_node)
}
if (BKE_image_has_packedfile(tex_image)) {
/* Put image in the same directory as the .MTL file. */
/* Put image in the same directory as the `.MTL` file. */
const char *filename = BLI_path_basename(tex_image->filepath);
fprintf(stderr,
"Packed image found:'%s'. Unpack and place the image in the same "

View File

@ -46,7 +46,7 @@ struct MTLTexMap {
};
/**
* Container suited for storing Material data for/from a .MTL file.
* Container suited for storing Material data for/from an `.MTL` file.
*/
struct MTLMaterial {
const MTLTexMap &tex_map_of_type(MTLTexMapType key) const

View File

@ -225,7 +225,7 @@ static void write_mesh_objects(const Span<std::unique_ptr<OBJMesh>> exportable_a
obj_writer.write_uv_coords(fh, obj);
}
/* This function takes a 0-indexed slot index for the obj_mesh object and
* returns the material name that we are using in the .obj file for it. */
* returns the material name that we are using in the `.obj` file for it. */
const auto *obj_mtlindices = mtlindices.is_empty() ? nullptr : &mtlindices[i];
auto matname_fn = [&](int s) -> const char * {
if (!obj_mtlindices || s < 0 || s >= obj_mtlindices->size()) {
@ -319,7 +319,9 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
write_nurbs_curve_objects(exportable_as_nurbs, *frame_writer);
}
bool append_frame_to_filename(const char *filepath, const int frame, char *r_filepath_with_frames)
bool append_frame_to_filename(const char *filepath,
const int frame,
char r_filepath_with_frames[1024])
{
BLI_strncpy(r_filepath_with_frames, filepath, FILE_MAX);
BLI_path_extension_strip(r_filepath_with_frames);

View File

@ -37,7 +37,7 @@ class OBJDepsgraph : NonMovable, NonCopyable {
};
/**
* The main function for exporting a .obj file according to the given `export_parameters`.
* The main function for exporting a `.obj` file according to the given `export_parameters`.
* It uses the context `C` to get the dependency graph, and from that, the `Scene`.
* Depending on whether or not `export_params.export_animation` is set, it writes
* either one file per animation frame, or just one file.
@ -51,15 +51,15 @@ class OBJMesh;
class OBJCurve;
/**
* Export a single frame of a .obj file, according to the given `export_parameters`.
* Export a single frame of a `.obj` file, according to the given `export_parameters`.
* The frame state is given in `depsgraph`.
* The output file name is given by `filepath`.
* This function is normally called from `exporter_main`, but is exposed here for testing purposes.
*/
/**
* Export a single frame to a .OBJ file.
* Export a single frame to a `.OBJ` file.
*
* Conditionally write a .MTL file also.
* Conditionally write a `.MTL` file also.
*/
void export_frame(Depsgraph *depsgraph,
const OBJExportParams &export_params,
@ -72,22 +72,22 @@ void export_frame(Depsgraph *depsgraph,
* exported, else all objects are to be exported. But only objects of type `OB_MESH`,
* `OB_CURVES_LEGACY`, and `OB_SURF` are supported; the rest will be ignored. If
* `export_params.export_curves_as_nurbs` is set, then curves of type `CU_NURBS` are exported in
* curve form in the .obj file, otherwise they are converted to mesh and returned in the `OBJMesh`
* vector. All other exportable types are always converted to mesh and returned in the `OBJMesh`
* vector.
* curve form in the `.obj` file, otherwise they are converted to mesh and returned in the
* `OBJMesh` vector. All other exportable types are always converted to mesh and returned in the
* `OBJMesh` vector.
*/
std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>>
filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params);
/**
* Makes `r_filepath_with_frames` (which should point at a character array of size `FILE_MAX`)
* be `filepath` with its "#" characters replaced by the number representing `frame`, and with
* a .obj extension.
*/
/**
* Append the current frame number in the .OBJ file name.
* Append the current frame number in the `.OBJ` file name.
*
* \return Whether the filepath is in #FILE_MAX limits.
* \param r_filepath_with_frames: The result of the `filepath` with its "#" characters
* replaced by the number representing `frame`, and with an `.obj` extension.
*
* \return Whether the `filepath` is in #FILE_MAX limits.
*/
bool append_frame_to_filename(const char *filepath, int frame, char *r_filepath_with_frames);
bool append_frame_to_filename(const char *filepath,
int frame,
char r_filepath_with_frames[1024 /*FILE_MAX*/]);
} // namespace blender::io::obj

View File

@ -849,8 +849,8 @@ void OBJParser::add_mtl_library(StringRef path)
void OBJParser::add_default_mtl_library()
{
/* Add any existing .mtl file that's with the same base name as the .obj file
* into candidate .mtl files to search through. This is not technically following the
/* Add any existing `.mtl` file that's with the same base name as the `.obj` file
* into candidate `.mtl` files to search through. This is not technically following the
* spec, but the old python importer was doing it, and there are user files out there
* that contain "mtllib bar.mtl" for a foo.obj, and depend on finding materials
* from foo.mtl (see #97757). */

View File

@ -300,7 +300,7 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh)
uv_map.finish();
/* If we have an object without UVs which resides in the same .obj file
/* If we have an object without UVs which resides in the same `.obj` file
* as an object which *does* have UVs we can end up adding a UV layer
* filled with zeroes.
* We could maybe check before creating this layer but that would need

View File

@ -542,7 +542,7 @@ TEST_F(OBJImportTest, import_all_objects)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
/* .obj file has empty EmptyText and EmptyMesh objects; these are ignored and skipped */
/* `.obj` file has empty EmptyText and EmptyMesh objects; these are ignored and skipped. */
{"OBBezierCurve", OB_MESH, 13, 12, 0, 0, float3(-1, -2, 0), float3(1, -2, 0)},
{"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, -1), float3(-1, 1, 1), float3(0, 0, 1)},
{"OBMaterialCube",

View File

@ -9,7 +9,7 @@
*
* \section aboutmakesdnac About makesdna tool
*
* `makesdna` creates a .c file with a long string of numbers that
* `makesdna` creates a `.c` file with a long string of numbers that
* encode the Blender file format. It is fast, because it is basically
* a binary dump. There are some details to mind when reconstructing
* the file (endianness and byte-alignment).

View File

@ -610,7 +610,7 @@ static const EnumPropertyItem *rna_Fluid_cachetype_volume_itemf(bContext * /*C*/
RNA_enum_item_add(&item, &totitem, &tmp);
# endif
/* Support for deprecated .raw format. */
/* Support for deprecated `.raw` format. */
FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
if (fds->cache_data_format == FLUID_DOMAIN_FILE_RAW ||
fds->cache_noise_format == FLUID_DOMAIN_FILE_RAW)

View File

@ -1145,6 +1145,28 @@ static bool asset_shelf_poll(const bContext *C, const AssetShelfType *shelf_type
return is_visible;
}
static const AssetWeakReference *asset_shelf_get_active_asset(const AssetShelfType *shelf_type)
{
extern FunctionRNA rna_AssetShelf_get_active_asset_func;
PointerRNA ptr = RNA_pointer_create(nullptr, shelf_type->rna_ext.srna, nullptr); /* dummy */
FunctionRNA *func = &rna_AssetShelf_get_active_asset_func;
ParameterList list;
RNA_parameter_list_create(&list, &ptr, func);
shelf_type->rna_ext.call(nullptr, &ptr, func, &list);
void *ret;
RNA_parameter_get_lookup(&list, "asset_reference", &ret);
/* Get the value before freeing. */
AssetWeakReference *active_asset = *(AssetWeakReference **)ret;
RNA_parameter_list_free(&list);
return active_asset;
}
static void asset_shelf_draw_context_menu(const bContext *C,
const AssetShelfType *shelf_type,
const AssetRepresentationHandle *asset,
@ -1175,23 +1197,12 @@ static bool rna_AssetShelf_unregister(Main *bmain, StructRNA *type)
return false;
}
SpaceType *space_type = BKE_spacetype_from_id(shelf_type->space_type);
if (!space_type) {
return false;
}
blender::ed::asset::shelf::type_unlink(*bmain, *shelf_type);
RNA_struct_free_extension(type, &shelf_type->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
const auto it = std::find_if(
space_type->asset_shelf_types.begin(),
space_type->asset_shelf_types.end(),
[&](const std::unique_ptr<AssetShelfType> &type) { return type.get() == shelf_type; });
BLI_assert(it != space_type->asset_shelf_types.end());
space_type->asset_shelf_types.remove(it - space_type->asset_shelf_types.begin());
blender::ed::asset::shelf::type_unregister(*shelf_type);
/* update while blender is running */
WM_main_add_notifier(NC_WINDOW, nullptr);
@ -1214,7 +1225,7 @@ static StructRNA *rna_AssetShelf_register(Main *bmain,
dummy_shelf.type = shelf_type.get();
PointerRNA dummy_shelf_ptr = RNA_pointer_create(nullptr, &RNA_AssetShelf, &dummy_shelf);
bool have_function[3];
bool have_function[4];
/* validate the python class */
if (validate(&dummy_shelf_ptr, data, have_function) != 0) {
@ -1230,27 +1241,21 @@ static StructRNA *rna_AssetShelf_register(Main *bmain,
return nullptr;
}
SpaceType *space_type = BKE_spacetype_from_id(shelf_type->space_type);
if (!space_type) {
BLI_assert_unreachable();
return nullptr;
}
/* Check if we have registered this asset shelf type before, and remove it. */
for (std::unique_ptr<AssetShelfType> &iter_shelf_type : space_type->asset_shelf_types) {
if (STREQ(iter_shelf_type->idname, shelf_type->idname)) {
if (iter_shelf_type->rna_ext.srna) {
BKE_reportf(reports,
RPT_INFO,
"Registering asset shelf class: '%s' has been registered before, "
"unregistering previous",
shelf_type->idname);
{
AssetShelfType *existing_shelf_type = blender::ed::asset::shelf::type_find_from_idname(
shelf_type->idname);
if (existing_shelf_type && existing_shelf_type->rna_ext.srna) {
BKE_reportf(reports,
RPT_INFO,
"Registering asset shelf class: '%s' has been registered before, "
"unregistering previous",
shelf_type->idname);
rna_AssetShelf_unregister(bmain, iter_shelf_type->rna_ext.srna);
}
break;
rna_AssetShelf_unregister(bmain, existing_shelf_type->rna_ext.srna);
}
}
if (!RNA_struct_available_or_report(reports, shelf_type->idname)) {
return nullptr;
}
@ -1267,11 +1272,12 @@ static StructRNA *rna_AssetShelf_register(Main *bmain,
shelf_type->poll = have_function[0] ? asset_shelf_poll : nullptr;
shelf_type->asset_poll = have_function[1] ? asset_shelf_asset_poll : nullptr;
shelf_type->draw_context_menu = have_function[2] ? asset_shelf_draw_context_menu : nullptr;
shelf_type->get_active_asset = have_function[2] ? asset_shelf_get_active_asset : nullptr;
shelf_type->draw_context_menu = have_function[3] ? asset_shelf_draw_context_menu : nullptr;
StructRNA *srna = shelf_type->rna_ext.srna;
space_type->asset_shelf_types.append(std::move(shelf_type));
blender::ed::asset::shelf::type_register(std::move(shelf_type));
/* update while blender is running */
WM_main_add_notifier(NC_WINDOW, nullptr);
@ -1279,6 +1285,24 @@ static StructRNA *rna_AssetShelf_register(Main *bmain,
return srna;
}
static void rna_AssetShelf_activate_operator_get(PointerRNA *ptr, char *value)
{
AssetShelf *shelf = static_cast<AssetShelf *>(ptr->data);
strcpy(value, shelf->type->activate_operator.c_str());
}
static int rna_AssetShelf_activate_operator_length(PointerRNA *ptr)
{
AssetShelf *shelf = static_cast<AssetShelf *>(ptr->data);
return shelf->type->activate_operator.size();
}
static void rna_AssetShelf_activate_operator_set(PointerRNA *ptr, const char *value)
{
AssetShelf *shelf = static_cast<AssetShelf *>(ptr->data);
shelf->type->activate_operator = value;
}
static StructRNA *rna_AssetShelf_refine(PointerRNA *shelf_ptr)
{
AssetShelf *shelf = (AssetShelf *)shelf_ptr->data;
@ -2309,6 +2333,17 @@ static void rna_def_asset_shelf(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG);
RNA_def_property_ui_text(prop, "Options", "Options for this asset shelf type");
prop = RNA_def_property(srna, "bl_activate_operator", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop,
"rna_AssetShelf_activate_operator_get",
"rna_AssetShelf_activate_operator_length",
"rna_AssetShelf_activate_operator_set");
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(
prop,
"Activate Operator",
"Operator to call when activating an item with asset reference properties");
prop = RNA_def_property(srna, "bl_default_preview_size", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "type->default_preview_size");
RNA_def_property_range(prop, 32, 256);
@ -2337,6 +2372,19 @@ static void rna_def_asset_shelf(BlenderRNA *brna)
parm = RNA_def_pointer(func, "asset", "AssetRepresentation", "", "");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
func = RNA_def_function(srna, "get_active_asset", nullptr);
RNA_def_function_ui_description(
func,
"Return a reference to the asset that should be highlighted as active in the asset shelf");
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
/* return type */
parm = RNA_def_pointer(func,
"asset_reference",
"AssetWeakReference",
"",
"The weak reference to the asset to be hightlighted as active, or None");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "draw_context_menu", nullptr);
RNA_def_function_ui_description(
func, "Draw UI elements into the context menu UI layout displayed on right click");

View File

@ -1102,11 +1102,12 @@ PyObject *PyC_DefaultNameSpace(const char *filename)
PyDict_SetItemString(modules, "__main__", mod_main);
Py_DECREF(mod_main); /* `sys.modules` owns now. */
PyModule_AddStringConstant(mod_main, "__name__", "__main__");
if (filename) {
/* __file__ mainly for nice UI'ness
* NOTE: this won't map to a real file when executing text-blocks and buttons. */
PyModule_AddObject(mod_main, "__file__", PyC_UnicodeFromBytes(filename));
}
/* This won't map to a real file when executing text-blocks and buttons.
* In this case an identifier is typically used that is surrounded by angle-brackets.
* It's mainly helpful for the UI and messages to show *something*. */
PyModule_AddObject(mod_main, "__file__", PyC_UnicodeFromBytes(filename));
PyModule_AddObject(mod_main, "__builtins__", builtins);
Py_INCREF(builtins); /* AddObject steals a reference */
return PyModule_GetDict(mod_main);
@ -1132,7 +1133,7 @@ bool PyC_NameSpace_ImportArray(PyObject *py_dict, const char *imports[])
return true;
}
void PyC_MainModule_Backup(PyObject **r_main_mod)
PyObject *PyC_MainModule_Backup()
{
PyObject *modules = PyImport_GetModuleDict();
PyObject *main_mod = PyDict_GetItemString(modules, "__main__");
@ -1141,7 +1142,7 @@ void PyC_MainModule_Backup(PyObject **r_main_mod)
/* is transferred back to `sys.modules`. */
Py_INCREF(main_mod);
}
*r_main_mod = main_mod;
return main_mod;
}
void PyC_MainModule_Restore(PyObject *main_mod)
@ -1466,45 +1467,62 @@ PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag)
/** \name Run String (Evaluate to Primitive Types)
* \{ */
static PyObject *pyc_run_string_as_py_object(const char *imports[],
const char *imports_star[],
const char *expr,
const char *filename)
ATTR_NONNULL(3, 4) ATTR_WARN_UNUSED_RESULT;
static PyObject *pyc_run_string_as_py_object(const char *imports[],
const char *imports_star[],
const char *expr,
const char *filename)
{
PyObject *main_mod = PyC_MainModule_Backup();
PyObject *py_dict = PyC_DefaultNameSpace(filename);
if (imports_star) {
for (int i = 0; imports_star[i]; i++) {
PyObject *mod = PyImport_ImportModule("math");
if (mod) {
/* Don't overwrite existing values (override=0). */
PyDict_Merge(py_dict, PyModule_GetDict(mod), 0);
Py_DECREF(mod);
}
else { /* Highly unlikely but possibly. */
PyErr_Print();
PyErr_Clear();
}
}
}
PyObject *retval;
if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
retval = nullptr; /* Failure. */
}
else {
retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict);
}
PyC_MainModule_Restore(main_mod);
return retval;
}
bool PyC_RunString_AsNumber(const char *imports[],
const char *expr,
const char *filename,
double *r_value)
{
PyObject *py_dict, *mod, *retval;
bool ok = true;
PyObject *main_mod = nullptr;
PyC_MainModule_Backup(&main_mod);
py_dict = PyC_DefaultNameSpace(filename);
mod = PyImport_ImportModule("math");
if (mod) {
PyDict_Merge(py_dict, PyModule_GetDict(mod), 0); /* 0 - don't overwrite existing values */
Py_DECREF(mod);
}
else { /* highly unlikely but possibly */
PyErr_Print();
PyErr_Clear();
}
if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
ok = false;
}
else if ((retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict)) == nullptr) {
ok = false;
}
else {
int ok = -1;
const char *imports_star[] = {"math", nullptr};
if (PyObject *retval = pyc_run_string_as_py_object(imports, imports_star, expr, filename)) {
double val;
if (PyTuple_Check(retval)) {
/* Users my have typed in 10km, 2m
* add up all values */
int i;
/* Users my have typed in `10km, 2m`, accumulate all values. */
val = 0.0;
for (i = 0; i < PyTuple_GET_SIZE(retval); i++) {
for (int i = 0; i < PyTuple_GET_SIZE(retval); i++) {
const double val_item = PyFloat_AsDouble(PyTuple_GET_ITEM(retval, i));
if (val_item == -1 && PyErr_Occurred()) {
val = -1;
@ -1523,15 +1541,18 @@ bool PyC_RunString_AsNumber(const char *imports[],
}
else if (!isfinite(val)) {
*r_value = 0.0;
ok = true;
}
else {
*r_value = val;
ok = true;
}
}
PyC_MainModule_Restore(main_mod);
return ok;
else {
ok = false;
}
BLI_assert(ok != -1);
return bool(ok);
}
bool PyC_RunString_AsIntPtr(const char *imports[],
@ -1539,36 +1560,23 @@ bool PyC_RunString_AsIntPtr(const char *imports[],
const char *filename,
intptr_t *r_value)
{
PyObject *py_dict, *retval;
bool ok = true;
PyObject *main_mod = nullptr;
PyC_MainModule_Backup(&main_mod);
py_dict = PyC_DefaultNameSpace(filename);
if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
ok = false;
}
else if ((retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict)) == nullptr) {
ok = false;
}
else {
intptr_t val;
val = intptr_t(PyLong_AsVoidPtr(retval));
int ok = -1;
if (PyObject *retval = pyc_run_string_as_py_object(imports, nullptr, expr, filename)) {
const intptr_t val = intptr_t(PyLong_AsVoidPtr(retval));
if (val == 0 && PyErr_Occurred()) {
ok = false;
}
else {
*r_value = val;
ok = true;
}
Py_DECREF(retval);
}
PyC_MainModule_Restore(main_mod);
else {
ok = false;
}
BLI_assert(ok != -1);
return ok;
}
@ -1578,25 +1586,10 @@ bool PyC_RunString_AsStringAndSize(const char *imports[],
char **r_value,
size_t *r_value_size)
{
PyObject *py_dict, *retval;
bool ok = true;
PyObject *main_mod = nullptr;
PyC_MainModule_Backup(&main_mod);
py_dict = PyC_DefaultNameSpace(filename);
if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
ok = false;
}
else if ((retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict)) == nullptr) {
ok = false;
}
else {
const char *val;
int ok = -1;
if (PyObject *retval = pyc_run_string_as_py_object(imports, nullptr, expr, filename)) {
Py_ssize_t val_len;
val = PyUnicode_AsUTF8AndSize(retval, &val_len);
const char *val = PyUnicode_AsUTF8AndSize(retval, &val_len);
if (val == nullptr && PyErr_Occurred()) {
ok = false;
}
@ -1605,13 +1598,14 @@ bool PyC_RunString_AsStringAndSize(const char *imports[],
memcpy(val_alloc, val, val_len + 1);
*r_value = val_alloc;
*r_value_size = val_len;
ok = true;
}
Py_DECREF(retval);
}
PyC_MainModule_Restore(main_mod);
else {
ok = false;
}
BLI_assert(ok != -1);
return ok;
}
@ -1630,30 +1624,16 @@ bool PyC_RunString_AsStringAndSizeOrNone(const char *imports[],
char **r_value,
size_t *r_value_size)
{
PyObject *py_dict, *retval;
bool ok = true;
PyObject *main_mod = nullptr;
PyC_MainModule_Backup(&main_mod);
py_dict = PyC_DefaultNameSpace(filename);
if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
ok = false;
}
else if ((retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict)) == nullptr) {
ok = false;
}
else {
int ok = -1;
if (PyObject *retval = pyc_run_string_as_py_object(imports, nullptr, expr, filename)) {
if (retval == Py_None) {
*r_value = nullptr;
*r_value_size = 0;
ok = true;
}
else {
const char *val;
Py_ssize_t val_len;
val = PyUnicode_AsUTF8AndSize(retval, &val_len);
const char *val = PyUnicode_AsUTF8AndSize(retval, &val_len);
if (val == nullptr && PyErr_Occurred()) {
ok = false;
}
@ -1662,15 +1642,16 @@ bool PyC_RunString_AsStringAndSizeOrNone(const char *imports[],
memcpy(val_alloc, val, val_len + 1);
*r_value = val_alloc;
*r_value_size = val_len;
ok = true;
}
}
Py_DECREF(retval);
}
PyC_MainModule_Restore(main_mod);
return ok;
else {
ok = false;
}
BLI_assert(ok != -1);
return bool(ok);
}
bool PyC_RunString_AsStringOrNone(const char *imports[],
@ -1808,10 +1789,6 @@ uint32_t PyC_Long_AsU32(PyObject *value)
return uint32_t(test);
}
/* #PyLong_AsUnsignedLongLong, unlike #PyLong_AsLongLong, does not fall back to calling
* #PyNumber_Index when its argument is not a `PyLongObject` instance. To match parsing signed
* integer types with #PyLong_AsLongLong, this function performs the #PyNumber_Index fallback, if
* necessary, before calling #PyLong_AsUnsignedLongLong. */
uint64_t PyC_Long_AsU64(PyObject *value)
{
if (value == nullptr) {

View File

@ -169,11 +169,11 @@ int PyC_ParseUnicodeAsBytesAndSize_OrNone(PyObject *o, void *p);
* >> print(__import__("__main__").foo)
*
* NOTE: this overwrites __main__ which gives problems with nested calls.
* be sure to run PyC_MainModule_Backup & PyC_MainModule_Restore if there is
* be sure to run #PyC_MainModule_Backup & #PyC_MainModule_Restore if there is
* any chance that python is in the call stack.
*/
PyObject *PyC_DefaultNameSpace(const char *filename);
void PyC_RunQuicky(const char *filepath, int n, ...);
PyObject *PyC_DefaultNameSpace(const char *filename) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
void PyC_RunQuicky(const char *filepath, int n, ...) ATTR_NONNULL(1);
/**
* Import `imports` into `py_dict`.
*
@ -187,7 +187,7 @@ bool PyC_NameSpace_ImportArray(PyObject *py_dict, const char *imports[]);
/**
* #PyC_MainModule_Restore MUST be called after #PyC_MainModule_Backup.
*/
void PyC_MainModule_Backup(PyObject **r_main_mod);
PyObject *PyC_MainModule_Backup() ATTR_WARN_UNUSED_RESULT;
void PyC_MainModule_Restore(PyObject *main_mod);
bool PyC_IsInterpreterActive(void);
@ -223,11 +223,11 @@ PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag);
bool PyC_RunString_AsNumber(const char **imports,
const char *expr,
const char *filename,
double *r_value);
double *r_value) ATTR_NONNULL(2, 3, 4) ATTR_WARN_UNUSED_RESULT;
bool PyC_RunString_AsIntPtr(const char **imports,
const char *expr,
const char *filename,
intptr_t *r_value);
intptr_t *r_value) ATTR_NONNULL(2, 3, 4) ATTR_WARN_UNUSED_RESULT;
/**
* \param r_value_size: The length of the string assigned: `strlen(*r_value)`.
*/
@ -235,11 +235,12 @@ bool PyC_RunString_AsStringAndSize(const char **imports,
const char *expr,
const char *filename,
char **r_value,
size_t *r_value_size);
size_t *r_value_size)
ATTR_NONNULL(2, 3, 4, 5) ATTR_WARN_UNUSED_RESULT;
bool PyC_RunString_AsString(const char **imports,
const char *expr,
const char *filename,
char **r_value);
char **r_value) ATTR_NONNULL(2, 3, 4) ATTR_WARN_UNUSED_RESULT;
/**
* \param r_value_size: The length of the string assigned: `strlen(*r_value)`.
@ -248,11 +249,12 @@ bool PyC_RunString_AsStringAndSizeOrNone(const char **imports,
const char *expr,
const char *filename,
char **r_value,
size_t *r_value_size);
size_t *r_value_size)
ATTR_NONNULL(2, 3, 4) ATTR_WARN_UNUSED_RESULT;
bool PyC_RunString_AsStringOrNone(const char **imports,
const char *expr,
const char *filename,
char **r_value);
char **r_value) ATTR_NONNULL(2, 3, 4) ATTR_WARN_UNUSED_RESULT;
/**
* Use with PyArg_ParseTuple's "O&" formatting.
@ -315,13 +317,21 @@ int32_t PyC_Long_AsI32(PyObject *value);
int64_t PyC_Long_AsI64(PyObject *value);
#endif
/* Unlike Python's #PyLong_AsUnsignedLong and #PyLong_AsUnsignedLongLong, these unsigned integer
/**
* Unlike Python's #PyLong_AsUnsignedLong and #PyLong_AsUnsignedLongLong, these unsigned integer
* parsing functions fall back to calling #PyNumber_Index when their argument is not a
* `PyLongObject`. This matches Python's signed integer parsing functions which also fall back to
* calling #PyNumber_Index. */
* calling #PyNumber_Index.
*/
uint8_t PyC_Long_AsU8(PyObject *value);
uint16_t PyC_Long_AsU16(PyObject *value);
uint32_t PyC_Long_AsU32(PyObject *value);
/**
* #PyLong_AsUnsignedLongLong, unlike #PyLong_AsLongLong, does not fall back to calling
* #PyNumber_Index when its argument is not a `PyLongObject` instance. To match parsing signed
* integer types with #PyLong_AsLongLong, this function performs the #PyNumber_Index fallback, if
* necessary, before calling #PyLong_AsUnsignedLongLong.
*/
uint64_t PyC_Long_AsU64(PyObject *value);
/* inline so type signatures match as expected */

View File

@ -144,7 +144,6 @@ static bool python_script_exec(
bContext *C, const char *filepath, Text *text, ReportList *reports, const bool do_jump)
{
Main *bmain_old = CTX_data_main(C);
PyObject *main_mod = nullptr;
PyObject *py_dict = nullptr, *py_result = nullptr;
PyGILState_STATE gilstate;
@ -160,7 +159,7 @@ static bool python_script_exec(
bpy_context_set(C, &gilstate);
PyC_MainModule_Backup(&main_mod);
PyObject *main_mod = PyC_MainModule_Backup();
if (text) {
bpy_text_filepath_get(filepath_dummy, sizeof(filepath_dummy), bmain_old, text);
@ -278,7 +277,6 @@ static bool bpy_run_string_impl(bContext *C,
{
BLI_assert(expr);
PyGILState_STATE gilstate;
PyObject *main_mod = nullptr;
PyObject *py_dict, *retval;
bool ok = true;
@ -288,7 +286,7 @@ static bool bpy_run_string_impl(bContext *C,
bpy_context_set(C, &gilstate);
PyC_MainModule_Backup(&main_mod);
PyObject *main_mod = PyC_MainModule_Backup();
py_dict = PyC_DefaultNameSpace("<blender string>");

View File

@ -31,8 +31,7 @@ static PyObject *bpy_rna_uilayout_introspect(PyObject *self)
uiLayout *layout = static_cast<uiLayout *>(pyrna->ptr.data);
const char *expr = UI_layout_introspect(layout);
PyObject *main_mod = nullptr;
PyC_MainModule_Backup(&main_mod);
PyObject *main_mod = PyC_MainModule_Backup();
PyObject *py_dict = PyC_DefaultNameSpace("<introspect>");
PyObject *result = PyRun_String(expr, Py_eval_input, py_dict, py_dict);
MEM_freeN((void *)expr);

View File

@ -81,8 +81,8 @@ static void wm_uilisttype_unlink_from_area(const uiListType *ult, ScrArea *area)
* For all lists representing \a ult, clear their `uiListType` pointer. Use when a list-type is
* deleted, so that the UI doesn't keep references to it.
*
* This is a common pattern for unregistering (usually .py defined) types at runtime, e.g. see
* #WM_gizmomaptype_group_unlink().
* This is a common pattern for unregistering (usually `.py` defined) types at runtime, e.g.
* see #WM_gizmomaptype_group_unlink().
* Note that unlike in some other cases using this pattern, we don't actually free the lists with
* type \a ult, we just clear the reference to the type. That's because UI-Lists are written to
* files and we don't want them to get lost together with their (user visible) settings.