Node Editor: Add overlay to automatically label reroute nodes #113368
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'}:
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ®ion,
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -99,16 +99,14 @@ Mesh *convert_ply_to_mesh(PlyData &data, const PLYImportParams ¶ms)
|
|||
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 ¶ms)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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(
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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>");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue