Brush Assets: Add catalog option to asset pushing operator #118382

Merged
Hans Goudey merged 26 commits from HooglyBoogly/blender:brush-assets-save-catalog-option into brush-assets-project 2024-02-21 14:03:41 +01:00
66 changed files with 1640 additions and 388 deletions
Showing only changes of commit 4ccf54c675 - Show all commits

View File

@ -55,6 +55,7 @@ import pxr.Gf as Gf
import pxr.Sdf as Sdf
import pxr.Usd as Usd
import pxr.UsdShade as UsdShade
import textwrap
class USDHookExample(bpy.types.USDHook):

View File

@ -102,7 +102,8 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
char *GHOST_DropTargetX11::FileUrlDecode(const char *fileUrl)
{
if (strncmp(fileUrl, "file://", 7) == 0) {
return GHOST_URL_decode_alloc(fileUrl + 7);
const char *file = fileUrl + 7;
return GHOST_URL_decode_alloc(file, strlen(file));
}
return nullptr;

View File

@ -12,6 +12,7 @@
#include <cstdlib>
#include <cstring>
#include "GHOST_Debug.hh"
#include "GHOST_PathUtils.hh"
#include "GHOST_Types.h"
@ -24,9 +25,10 @@ using DecodeState_e = enum DecodeState_e {
STATE_CONVERTING
};
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src)
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src, const int buf_src_len)
{
const uint buf_src_len = strlen(buf_src);
GHOST_ASSERT(strnlen(buf_src, buf_src_len) == buf_src_len, "Incorrect length");
DecodeState_e state = STATE_SEARCH;
uint ascii_character;
@ -85,12 +87,12 @@ void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src)
}
}
char *GHOST_URL_decode_alloc(const char *buf_src)
char *GHOST_URL_decode_alloc(const char *buf_src, const int buf_src_len)
{
/* Assume one character of encoded URL can be expanded to 4 chars max. */
const size_t decoded_size_max = 4 * strlen(buf_src) + 1;
const size_t decoded_size_max = 4 * buf_src_len + 1;
char *buf_dst = (char *)malloc(decoded_size_max);
GHOST_URL_decode(buf_dst, decoded_size_max, buf_src);
GHOST_URL_decode(buf_dst, decoded_size_max, buf_src, buf_src_len);
const size_t decoded_size = strlen(buf_dst) + 1;
if (decoded_size != decoded_size_max) {
char *buf_dst_trim = (char *)malloc(decoded_size);

View File

@ -14,12 +14,13 @@
* \param buf_dst: Buffer for decoded URL.
* \param buf_dst_maxlen: Size of output buffer.
* \param buf_src: Input encoded buffer to be decoded.
* \param buf_src_len: The length of `buf_src` to use.
*/
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src);
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src, int buf_src_len);
/**
* A version of #GHOST_URL_decode that allocates the string & returns it.
*
* \param buf_src: Input encoded buffer to be decoded.
* \return The decoded output buffer.
*/
char *GHOST_URL_decode_alloc(const char *buf_src);
char *GHOST_URL_decode_alloc(const char *buf_src, int buf_src_len);

View File

@ -2926,6 +2926,9 @@ static char *read_buffer_from_data_offer(GWL_DataOffer *data_offer,
}
close(pipefd[0]);
}
else {
*r_len = 0;
}
return buf;
}
@ -3259,20 +3262,19 @@ static void data_device_handle_drop(void *data, wl_data_device * /*wl_data_devic
CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive);
auto read_uris_fn = [](GWL_Seat *const seat,
GWL_DataOffer *data_offer,
wl_surface *wl_surface_window,
const char *mime_receive) {
auto read_drop_data_fn = [](GWL_Seat *const seat,
GWL_DataOffer *data_offer,
wl_surface *wl_surface_window,
const char *mime_receive) {
const uint64_t event_ms = seat->system->getMilliSeconds();
const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)};
const bool nil_terminate = (mime_receive != ghost_wl_mime_text_uri);
size_t data_buf_len = 0;
const char *data_buf = read_buffer_from_data_offer(
data_offer, mime_receive, nullptr, false, &data_buf_len);
std::string data = data_buf ? std::string(data_buf, data_buf_len) : "";
free(const_cast<char *>(data_buf));
data_offer, mime_receive, nullptr, nil_terminate, &data_buf_len);
CLOG_INFO(LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive, data.c_str());
CLOG_INFO(LOG, 2, "read_drop_data mime_receive=%s, data_len=%zu", mime_receive, data_buf_len);
wl_data_offer_finish(data_offer->wl.id);
wl_data_offer_destroy(data_offer->wl.id);
@ -3283,69 +3285,97 @@ static void data_device_handle_drop(void *data, wl_data_device * /*wl_data_devic
delete data_offer;
data_offer = nullptr;
GHOST_SystemWayland *const system = seat->system;
/* Don't generate a drop event if the data could not be read,
* an error will have been logged. */
if (data_buf != nullptr) {
GHOST_TDragnDropTypes ghost_dnd_type = GHOST_kDragnDropTypeUnknown;
void *ghost_dnd_data = nullptr;
if (mime_receive == ghost_wl_mime_text_uri) {
const char file_proto[] = "file://";
/* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`.
* So support both, once `\n` is found, strip the preceding `\r` if found. */
const char lf = '\n';
/* Failure to receive drop data . */
if (mime_receive == ghost_wl_mime_text_uri) {
const char file_proto[] = "file://";
/* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`.
* So support both, once `\n` is found, strip the preceding `\r` if found. */
const char lf = '\n';
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_window);
std::vector<std::string> uris;
const std::string_view data = std::string_view(data_buf, data_buf_len);
std::vector<std::string_view> uris;
size_t pos = 0;
while (pos != std::string::npos) {
pos = data.find(file_proto, pos);
if (pos == std::string::npos) {
break;
size_t pos = 0;
while (pos != std::string::npos) {
pos = data.find(file_proto, pos);
if (pos == std::string::npos) {
break;
}
const size_t start = pos + sizeof(file_proto) - 1;
pos = data.find(lf, pos);
size_t end = pos;
if (UNLIKELY(end == std::string::npos)) {
/* Note that most well behaved file managers will add a trailing newline,
* Gnome's web browser (44.3) doesn't, so support reading up until the last byte. */
end = data.size();
}
/* Account for 'CRLF' case. */
if (data[end - 1] == '\r') {
end -= 1;
}
std::string_view data_substr = data.substr(start, end - start);
uris.push_back(data_substr);
CLOG_INFO(LOG,
2,
"read_drop_data pos=%zu, text_uri=\"%.*s\"",
start,
int(data_substr.size()),
data_substr.data());
}
const size_t start = pos + sizeof(file_proto) - 1;
pos = data.find(lf, pos);
size_t end = pos;
if (UNLIKELY(end == std::string::npos)) {
/* Note that most well behaved file managers will add a trailing newline,
* Gnome's web browser (44.3) doesn't, so support reading up until the last byte. */
end = data.size();
GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>(
malloc(sizeof(GHOST_TStringArray)));
flist->count = int(uris.size());
flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
for (size_t i = 0; i < uris.size(); i++) {
flist->strings[i] = reinterpret_cast<uint8_t *>(
GHOST_URL_decode_alloc(uris[i].data(), uris[i].size()));
}
/* Account for 'CRLF' case. */
if (data[end - 1] == '\r') {
end -= 1;
}
uris.push_back(data.substr(start, end - start));
CLOG_INFO(LOG, 2, "drop_read_uris pos=%zu, text_uri=\"%s\"", start, uris.back().c_str());
CLOG_INFO(LOG, 2, "read_drop_data file_count=%d", flist->count);
ghost_dnd_type = GHOST_kDragnDropTypeFilenames;
ghost_dnd_data = flist;
}
else if (ELEM(mime_receive, ghost_wl_mime_text_plain, ghost_wl_mime_text_utf8)) {
ghost_dnd_type = GHOST_kDragnDropTypeString;
ghost_dnd_data = (void *)data_buf; /* Move ownership to the event. */
data_buf = nullptr;
}
GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>(
malloc(sizeof(GHOST_TStringArray)));
flist->count = int(uris.size());
flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
for (size_t i = 0; i < uris.size(); i++) {
flist->strings[i] = reinterpret_cast<uint8_t *>(GHOST_URL_decode_alloc(uris[i].c_str()));
if (ghost_dnd_type != GHOST_kDragnDropTypeUnknown) {
GHOST_SystemWayland *const system = seat->system;
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_window);
const int event_xy[2] = {WL_FIXED_TO_INT_FOR_WINDOW_V2(win, xy)};
system->pushEvent_maybe_pending(new GHOST_EventDragnDrop(event_ms,
GHOST_kEventDraggingDropDone,
ghost_dnd_type,
win,
UNPACK2(event_xy),
ghost_dnd_data));
wl_display_roundtrip(system->wl_display_get());
}
else {
CLOG_INFO(LOG, 2, "read_drop_data, unhandled!");
}
CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count);
const int event_xy[2] = {WL_FIXED_TO_INT_FOR_WINDOW_V2(win, xy)};
system->pushEvent_maybe_pending(new GHOST_EventDragnDrop(event_ms,
GHOST_kEventDraggingDropDone,
GHOST_kDragnDropTypeFilenames,
win,
UNPACK2(event_xy),
flist));
free(const_cast<char *>(data_buf));
}
else if (ELEM(mime_receive, ghost_wl_mime_text_plain, ghost_wl_mime_text_utf8)) {
/* TODO: enable use of internal functions 'txt_insert_buf' and
* 'text_update_edited' to behave like dropped text was pasted. */
CLOG_INFO(LOG, 2, "drop_read_uris_fn (text_plain, text_utf8), unhandled!");
}
wl_display_roundtrip(system->wl_display_get());
};
/* Pass in `seat->wl_surface_window_focus_dnd` instead of accessing it from `seat` since the
* leave callback (#data_device_handle_leave) will clear the value once this function starts. */
std::thread read_thread(
read_uris_fn, seat, data_offer, seat->wl.surface_window_focus_dnd, mime_receive);
read_drop_data_fn, seat, data_offer, seat->wl.surface_window_focus_dnd, mime_receive);
read_thread.detach();
}

View File

@ -43,7 +43,7 @@ class MotionPathButtonsPanel:
col.prop(mps, "frame_step", text="Step")
row = col.row()
row.prop(mps, "bake_in_camera_space", text="Bake to Active Camera")
row.prop(mps, "use_camera_space_bake", text="Bake to Active Camera")
if bones:
op_category = "pose"

View File

@ -48,8 +48,13 @@ class GREASE_PENCIL_MT_grease_pencil_add_layer_extra(Menu):
def draw(self, context):
layout = self.layout
space = context.space_data
if space.type == 'PROPERTIES':
layout.operator("grease_pencil.layer_group_add", text="Add Group")
layout.operator("grease_pencil.layer_group_add", text="Add Group")
layout.separator()
layout.operator("grease_pencil.layer_duplicate", text="Duplicate", icon='DUPLICATE')
layout.operator("grease_pencil.layer_duplicate", text="Duplicate Empty Keyframes").empty_keyframes = True
layout.separator()
layout.operator("grease_pencil.layer_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")

View File

@ -71,7 +71,7 @@ class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
if geometry_nodes_supported:
self.operator_modifier_add(layout, 'NODES')
layout.separator()
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_edit")
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME', 'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_generate")
@ -107,6 +107,8 @@ class OBJECT_MT_modifier_add_edit(ModifierAddMenu, Menu):
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_EDIT')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_PROXIMITY')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_VERTEX_WEIGHT_ANGLE')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
@ -150,6 +152,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'WIREFRAME')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARRAY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_DASH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR')

View File

@ -159,7 +159,7 @@ class GRAPH_MT_view(Menu):
layout.prop(st, "use_realtime_update")
layout.prop(st, "show_sliders")
layout.prop(st, "use_auto_merge_keyframes")
layout.prop(st, "autolock_translation_axis")
layout.prop(st, "use_auto_lock_translation_axis")
layout.separator()
if st.mode != 'DRIVERS':

View File

@ -39,7 +39,7 @@ class AssetRepresentation {
*/
const bool is_local_id_ = false;
/** Asset library that owns this asset representation. */
const AssetLibrary *owner_asset_library_;
const AssetLibrary &owner_asset_library_;
struct ExternalAsset {
std::string name;
@ -67,16 +67,12 @@ class AssetRepresentation {
AssetRepresentation(AssetIdentifier &&identifier,
ID &id,
const AssetLibrary &owner_asset_library);
AssetRepresentation(AssetRepresentation &&other);
/* Non-copyable type. */
AssetRepresentation(const AssetRepresentation &other) = delete;
~AssetRepresentation();
/* Non-move-assignable type. Move construction is fine, but treat the "identity" (e.g. local vs
* external asset) of an asset representation as immutable. */
AssetRepresentation &operator=(AssetRepresentation &&other) = delete;
/* Non-copyable type. */
AssetRepresentation &operator=(const AssetRepresentation &other) = delete;
AssetRepresentation(const AssetRepresentation &) = delete;
AssetRepresentation(AssetRepresentation &&) = delete;
AssetRepresentation &operator=(AssetRepresentation &&) = delete;
AssetRepresentation &operator=(const AssetRepresentation &) = delete;
const AssetIdentifier &get_identifier() const;

View File

@ -10,7 +10,6 @@
#include "DNA_ID.h"
#include "DNA_asset_types.h"
#include "DNA_userdef_types.h"
#include "AS_asset_identifier.hh"
#include "AS_asset_library.hh"
@ -25,7 +24,7 @@ AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier,
const AssetLibrary &owner_asset_library)
: identifier_(identifier),
is_local_id_(false),
owner_asset_library_(&owner_asset_library),
owner_asset_library_(owner_asset_library),
external_asset_()
{
external_asset_.name = name;
@ -38,7 +37,7 @@ AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier,
const AssetLibrary &owner_asset_library)
: identifier_(identifier),
is_local_id_(true),
owner_asset_library_(&owner_asset_library),
owner_asset_library_(owner_asset_library),
local_asset_id_(&id)
{
if (!id.asset_data) {
@ -46,18 +45,6 @@ AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier,
}
}
AssetRepresentation::AssetRepresentation(AssetRepresentation &&other)
: identifier_(std::move(other.identifier_)), is_local_id_(other.is_local_id_)
{
if (is_local_id_) {
local_asset_id_ = other.local_asset_id_;
other.local_asset_id_ = nullptr;
}
else {
external_asset_ = std::move(other.external_asset_);
}
}
AssetRepresentation::~AssetRepresentation()
{
if (!is_local_id_) {
@ -72,11 +59,7 @@ const AssetIdentifier &AssetRepresentation::get_identifier() const
AssetWeakReference *AssetRepresentation::make_weak_reference() const
{
if (!owner_asset_library_) {
return nullptr;
}
return AssetWeakReference::make_reference(*owner_asset_library_, identifier_);
return AssetWeakReference::make_reference(owner_asset_library_, identifier_);
}
StringRefNull AssetRepresentation::get_name() const
@ -104,26 +87,20 @@ AssetMetaData &AssetRepresentation::get_metadata() const
std::optional<eAssetImportMethod> AssetRepresentation::get_import_method() const
{
if (!owner_asset_library_) {
return {};
}
return owner_asset_library_->import_method_;
return owner_asset_library_.import_method_;
}
bool AssetRepresentation::may_override_import_method() const
{
if (!owner_asset_library_ || !owner_asset_library_->import_method_) {
if (!owner_asset_library_.import_method_) {
return true;
}
return owner_asset_library_->may_override_import_method_;
return owner_asset_library_.may_override_import_method_;
}
bool AssetRepresentation::get_use_relative_path() const
{
if (!owner_asset_library_) {
return false;
}
return owner_asset_library_->use_relative_path_;
return owner_asset_library_.use_relative_path_;
}
ID *AssetRepresentation::local_id() const
@ -138,7 +115,7 @@ bool AssetRepresentation::is_local_id() const
const AssetLibrary &AssetRepresentation::owner_asset_library() const
{
return *owner_asset_library_;
return owner_asset_library_;
}
} // namespace blender::asset_system

View File

@ -45,7 +45,7 @@ struct DupliObject {
float mat[4][4];
float orco[3], uv[2];
short type; /* from Object.transflag */
short type; /* From #Object::transflag. */
char no_draw;
/* If this dupli object is belongs to a preview, this is non-null. */
const blender::bke::GeometrySet *preview_base_geometry;

View File

@ -729,7 +729,9 @@ namespace convert {
void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
const ListBase &vertex_group_names,
GreasePencilDrawing &r_drawing);
void legacy_gpencil_to_grease_pencil(Main &main, GreasePencil &grease_pencil, bGPdata &gpd);
void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, bGPdata &gpd);
void legacy_gpencil_object(Main &bmain, Object &object);
} // namespace convert
} // namespace greasepencil

View File

@ -74,7 +74,7 @@ void BKE_ffmpeg_context_free(void *context_v);
void BKE_ffmpeg_exit();
/**
* Gets a libswscale context for given size and format parameters.
* Gets a `libswscale` context for given size and format parameters.
* After you're done using the context, call #BKE_ffmpeg_sws_release_context
* to release it. Internally the contexts are coming from the context
* pool/cache.

View File

@ -367,6 +367,11 @@ void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *use
* - various minor settings (add as needed).
*/
#define VALUE_SWAP(id) \
{ \
std::swap(userdef_a->id, userdef_b->id); \
}
#define DATA_SWAP(id) \
{ \
UserDef userdef_tmp; \
@ -387,12 +392,12 @@ void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *use
} \
((void)0)
std::swap(userdef_a->uistyles, userdef_b->uistyles);
std::swap(userdef_a->uifonts, userdef_b->uifonts);
std::swap(userdef_a->themes, userdef_b->themes);
std::swap(userdef_a->addons, userdef_b->addons);
std::swap(userdef_a->user_keymaps, userdef_b->user_keymaps);
std::swap(userdef_a->user_keyconfig_prefs, userdef_b->user_keyconfig_prefs);
VALUE_SWAP(uistyles);
VALUE_SWAP(uifonts);
VALUE_SWAP(themes);
VALUE_SWAP(addons);
VALUE_SWAP(user_keymaps);
VALUE_SWAP(user_keyconfig_prefs);
DATA_SWAP(font_path_ui);
DATA_SWAP(font_path_ui_mono);
@ -406,9 +411,8 @@ void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *use
DATA_SWAP(ui_scale);
#undef SWAP_TYPELESS
#undef VALUE_SWAP
#undef DATA_SWAP
#undef LISTBASE_SWAP
#undef FLAG_SWAP
}

View File

@ -10,7 +10,9 @@
#include "BKE_curves.hh"
#include "BKE_deform.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_id.hh"
#include "BKE_material.h"
#include "BKE_object.hh"
#include "BLI_color.hh"
#include "BLI_listbase.h"
@ -75,13 +77,23 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
/* Get the number of points, number of strokes and the offsets for each stroke. */
Vector<int> offsets;
Vector<int8_t> curve_types;
offsets.append(0);
int num_strokes = 0;
int num_points = 0;
bool has_bezier_stroke = false;
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf.strokes) {
num_points += gps->totpoints;
offsets.append(num_points);
if (gps->editcurve != nullptr) {
has_bezier_stroke = true;
num_points += gps->editcurve->tot_curve_points;
curve_types.append(CURVE_TYPE_BEZIER);
}
else {
num_points += gps->totpoints;
curve_types.append(CURVE_TYPE_POLY);
}
num_strokes++;
offsets.append(num_points);
}
/* Resize the CurvesGeometry. */
@ -94,8 +106,14 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
OffsetIndices<int> points_by_curve = curves.points_by_curve();
MutableAttributeAccessor attributes = curves.attributes_for_write();
/* All strokes are poly curves. */
curves.fill_curve_types(CURVE_TYPE_POLY);
if (!has_bezier_stroke) {
/* All strokes are poly curves. */
curves.fill_curve_types(CURVE_TYPE_POLY);
}
else {
curves.curve_types_for_write().copy_from(curve_types);
curves.update_curve_types();
}
/* Find used vertex groups in this drawing. */
ListBase stroke_vertex_group_names;
@ -118,6 +136,12 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
/* Point Attributes. */
MutableSpan<float3> positions = curves.positions_for_write();
MutableSpan<float3> handle_positions_left = has_bezier_stroke ?
curves.handle_positions_left_for_write() :
MutableSpan<float3>();
MutableSpan<float3> handle_positions_right = has_bezier_stroke ?
curves.handle_positions_right_for_write() :
MutableSpan<float3>();
MutableSpan<float> radii = drawing.radii_for_write();
MutableSpan<float> opacities = drawing.opacities_for_write();
SpanAttributeWriter<float> delta_times = attributes.lookup_or_add_for_write_span<float>(
@ -158,9 +182,6 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
int stroke_i = 0;
LISTBASE_FOREACH_INDEX (bGPDstroke *, gps, &gpf.strokes, stroke_i) {
/* TODO: check if `gps->editcurve` is not nullptr and parse bezier curve instead. */
/* Write curve attributes. */
stroke_cyclic.span[stroke_i] = (gps->flag & GP_STROKE_CYCLIC) != 0;
/* TODO: This should be a `double` attribute. */
stroke_init_times.span[stroke_i] = float(gps->inittime);
@ -175,59 +196,86 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
stroke_fill_colors.span[stroke_i] = ColorGeometry4f(gps->vert_color_fill);
stroke_materials.span[stroke_i] = gps->mat_nr;
/* Write point attributes. */
IndexRange stroke_points_range = points_by_curve[stroke_i];
if (stroke_points_range.is_empty()) {
IndexRange points = points_by_curve[stroke_i];
if (points.is_empty()) {
continue;
}
Span<bGPDspoint> stroke_points{gps->points, gps->totpoints};
MutableSpan<float3> stroke_positions = positions.slice(stroke_points_range);
MutableSpan<float> stroke_radii = radii.slice(stroke_points_range);
MutableSpan<float> stroke_opacities = opacities.slice(stroke_points_range);
MutableSpan<float> stroke_deltatimes = delta_times.span.slice(stroke_points_range);
MutableSpan<float> stroke_rotations = rotations.span.slice(stroke_points_range);
MutableSpan<ColorGeometry4f> stroke_vertex_colors = vertex_colors.span.slice(
stroke_points_range);
MutableSpan<bool> stroke_selections = selection.span.slice(stroke_points_range);
MutableSpan<MDeformVert> stroke_dverts = use_dverts ? dverts.slice(stroke_points_range) :
MutableSpan<MDeformVert>();
const Span<bGPDspoint> src_points{gps->points, gps->totpoints};
/* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px`
* was the brush size which would be stored in the stroke thickness and then scaled by the
* point pressure factor. Finally, the render engine would divide this thickness value by
* 2000 (we're going from a thickness to a radius, hence the factor of two) to convert back
* into blender units. Store the radius now directly in blender units. This makes it
* consistent with how hair curves handle the radius. */
const float stroke_thickness = float(gps->thickness) / 2000.0f;
MutableSpan<float3> dst_positions = positions.slice(points);
MutableSpan<float3> dst_handle_positions_left = has_bezier_stroke ?
handle_positions_left.slice(points) :
MutableSpan<float3>();
MutableSpan<float3> dst_handle_positions_right = has_bezier_stroke ?
handle_positions_right.slice(points) :
MutableSpan<float3>();
MutableSpan<float> dst_radii = radii.slice(points);
MutableSpan<float> dst_opacities = opacities.slice(points);
MutableSpan<float> dst_deltatimes = delta_times.span.slice(points);
MutableSpan<float> dst_rotations = rotations.span.slice(points);
MutableSpan<ColorGeometry4f> dst_vertex_colors = vertex_colors.span.slice(points);
MutableSpan<bool> dst_selection = selection.span.slice(points);
MutableSpan<MDeformVert> dst_dverts = use_dverts ? dverts.slice(points) :
MutableSpan<MDeformVert>();
/* Do first point. */
const bGPDspoint &first_pt = stroke_points.first();
stroke_positions.first() = float3(first_pt.x, first_pt.y, first_pt.z);
/* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px` was
* the brush size which would be stored in the stroke thickness and then scaled by the point
* pressure factor. Finally, the render engine would divide this thickness value by 2000 (we're
* going from a thickness to a radius, hence the factor of two) to convert back into blender
* units.
* Store the radius now directly in blender units. This makes it consistent with how hair
* curves handle the radius. */
stroke_radii.first() = gps->thickness * first_pt.pressure / 2000.0f;
stroke_opacities.first() = first_pt.strength;
stroke_deltatimes.first() = 0;
stroke_rotations.first() = first_pt.uv_rot;
stroke_vertex_colors.first() = ColorGeometry4f(first_pt.vert_color);
stroke_selections.first() = (first_pt.flag & GP_SPOINT_SELECT) != 0;
if (use_dverts && gps->dvert) {
copy_dvert(gps->dvert[0], stroke_dverts.first());
if (curve_types[stroke_i] == CURVE_TYPE_POLY) {
threading::parallel_for(src_points.index_range(), 4096, [&](const IndexRange range) {
for (const int point_i : range) {
const bGPDspoint &pt = src_points[point_i];
dst_positions[point_i] = float3(pt.x, pt.y, pt.z);
dst_radii[point_i] = stroke_thickness * pt.pressure;
dst_opacities[point_i] = pt.strength;
dst_rotations[point_i] = pt.uv_rot;
dst_vertex_colors[point_i] = ColorGeometry4f(pt.vert_color);
dst_selection[point_i] = (pt.flag & GP_SPOINT_SELECT) != 0;
if (use_dverts && gps->dvert) {
copy_dvert(gps->dvert[point_i], dst_dverts[point_i]);
}
}
});
dst_deltatimes.first() = 0;
threading::parallel_for(
src_points.index_range().drop_front(1), 4096, [&](const IndexRange range) {
for (const int point_i : range) {
const bGPDspoint &pt = src_points[point_i];
const bGPDspoint &pt_prev = src_points[point_i - 1];
dst_deltatimes[point_i] = pt.time - pt_prev.time;
}
});
}
else if (curve_types[stroke_i] == CURVE_TYPE_BEZIER) {
BLI_assert(gps->editcurve != nullptr);
Span<bGPDcurve_point> src_curve_points{gps->editcurve->curve_points,
gps->editcurve->tot_curve_points};
/* Do the rest of the points. */
for (const int i : stroke_points.index_range().drop_back(1)) {
const int point_i = i + 1;
const bGPDspoint &pt_prev = stroke_points[point_i - 1];
const bGPDspoint &pt = stroke_points[point_i];
stroke_positions[point_i] = float3(pt.x, pt.y, pt.z);
stroke_radii[point_i] = gps->thickness * pt.pressure / 2000.0f;
stroke_opacities[point_i] = pt.strength;
stroke_deltatimes[point_i] = pt.time - pt_prev.time;
stroke_rotations[point_i] = pt.uv_rot;
stroke_vertex_colors[point_i] = ColorGeometry4f(pt.vert_color);
stroke_selections[point_i] = (pt.flag & GP_SPOINT_SELECT) != 0;
if (use_dverts && gps->dvert) {
copy_dvert(gps->dvert[point_i], stroke_dverts[point_i]);
}
threading::parallel_for(src_curve_points.index_range(), 4096, [&](const IndexRange range) {
for (const int point_i : range) {
const bGPDcurve_point &cpt = src_curve_points[point_i];
dst_positions[point_i] = float3(cpt.bezt.vec[1]);
dst_handle_positions_left[point_i] = float3(cpt.bezt.vec[0]);
dst_handle_positions_right[point_i] = float3(cpt.bezt.vec[2]);
dst_radii[point_i] = stroke_thickness * cpt.pressure;
dst_opacities[point_i] = cpt.strength;
dst_rotations[point_i] = cpt.uv_rot;
dst_vertex_colors[point_i] = ColorGeometry4f(cpt.vert_color);
dst_selection[point_i] = (cpt.flag & GP_CURVE_POINT_SELECT) != 0;
if (use_dverts && gps->dvert) {
copy_dvert(gps->dvert[point_i], dst_dverts[point_i]);
}
}
});
}
else {
/* Unknown curve type. */
BLI_assert_unreachable();
}
}
@ -342,4 +390,24 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b
BKE_id_materials_copy(&bmain, &gpd.id, &grease_pencil.id);
}
void legacy_gpencil_object(Main &bmain, Object &object)
{
bGPdata *gpd = static_cast<bGPdata *>(object.data);
GreasePencil *new_grease_pencil = static_cast<GreasePencil *>(
BKE_id_new(&bmain, ID_GP, gpd->id.name + 2));
object.data = new_grease_pencil;
object.type = OB_GREASE_PENCIL;
/* NOTE: Could also use #BKE_id_free_us, to also free the legacy GP if not used anymore? */
id_us_min(&gpd->id);
/* No need to increase usercount of `new_grease_pencil`, since ID creation already set it
* to 1. */
legacy_gpencil_to_grease_pencil(bmain, *new_grease_pencil, *gpd);
BKE_object_free_derived_caches(&object);
BKE_object_free_modifiers(&object, 0);
}
} // namespace blender::bke::greasepencil::convert

View File

@ -107,6 +107,7 @@ static void id_type_init()
init_types_num++;
BLI_assert_msg(init_types_num == INDEX_ID_MAX, "Some IDTypeInfo initialization is missing");
UNUSED_VARS_NDEBUG(init_types_num);
#undef INIT_TYPE
}

View File

@ -4211,6 +4211,11 @@ void BKE_lib_override_library_validate(Main * /*bmain*/, ID *id, ReportList *rep
if (id->override_library == nullptr) {
return;
}
/* NOTE: In code deleting liboverride data below, #BKE_lib_override_library_make_local is used
* instead of directly calling #BKE_lib_override_library_free, because the former also handles
* properly 'liboverride embedded' IDs, like root nodetrees, or shapekeys. */
if (id->override_library->reference == nullptr) {
/* This (probably) used to be a template ID, could be linked or local, not an override. */
BKE_reportf(reports,
@ -4218,7 +4223,7 @@ void BKE_lib_override_library_validate(Main * /*bmain*/, ID *id, ReportList *rep
"Library override templates have been removed: removing all override data from "
"the data-block '%s'",
id->name);
BKE_lib_override_library_free(&id->override_library, true);
BKE_lib_override_library_make_local(nullptr, id);
return;
}
if (id->override_library->reference == id) {
@ -4229,7 +4234,7 @@ void BKE_lib_override_library_validate(Main * /*bmain*/, ID *id, ReportList *rep
"Data corruption: data-block '%s' is using itself as library override reference, "
"removing all override data",
id->name);
BKE_lib_override_library_free(&id->override_library, true);
BKE_lib_override_library_make_local(nullptr, id);
return;
}
if (!ID_IS_LINKED(id->override_library->reference)) {
@ -4241,7 +4246,7 @@ void BKE_lib_override_library_validate(Main * /*bmain*/, ID *id, ReportList *rep
"library override reference, removing all override data",
id->name,
id->override_library->reference->name);
BKE_lib_override_library_free(&id->override_library, true);
BKE_lib_override_library_make_local(nullptr, id);
return;
}
}

View File

@ -316,8 +316,8 @@ TEST(lib_remap, never_null_usage_storage_requested_on_delete)
remapper,
(ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_STORE_NEVER_NULL_USAGE));
/* Never null usages unassignement is not enforced (no #ID_REMAP_FORCE_NEVER_NULL_USAGE), so the
* obdta should still use the original mesh. */
/* Never null usages un-assignment is not enforced (no #ID_REMAP_FORCE_NEVER_NULL_USAGE),
* so the object-data should still use the original mesh. */
EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
EXPECT_NE(context.test_data.object->data, nullptr);
EXPECT_TRUE(remapper.never_null_users().contains(&context.test_data.object->id));

View File

@ -32,14 +32,14 @@ using namespace blender;
/** \name Internal Math Functions
* \{ */
static float mul_v2_v2_cw_x(const float2 &mat, const float2 &vec)
static float sincos_rotate_cw_x(const float2 &sincos, const float2 &p)
{
return (mat[0] * vec[0]) + (mat[1] * vec[1]);
return (sincos[0] * p[0]) + (sincos[1] * p[1]);
}
static float mul_v2_v2_cw_y(const float2 &mat, const float2 &vec)
static float sincos_rotate_cw_y(const float2 &sincos, const float2 &p)
{
return (mat[1] * vec[0]) - (mat[0] * vec[1]);
return (sincos[1] * p[0]) - (sincos[0] * p[1]);
}
/** \} */
@ -78,21 +78,23 @@ static int convexhull_2d_sorted(const float (*points)[2], const int points_num,
/* Array scan index. */
int i;
int minmin, minmax;
int maxmin, maxmax;
const int minmin = 0;
const int maxmax = points_num - 1;
int minmax;
int maxmin;
float xmax;
/* Get the indices of points with min X-coord and min|max Y-coord. */
float xmin = points[0][0];
for (i = 1; i < points_num; i++) {
for (i = 1; i <= maxmax; i++) {
if (points[i][0] != xmin) {
break;
}
}
minmin = 0;
minmax = i - 1;
if (minmax == points_num - 1) { /* Degenerate case: all x-coords == X-min. */
if (minmax == maxmax) { /* Degenerate case: all x-coords == X-min. */
r_points[++top] = minmin;
if (points[minmax][1] != points[minmin][1]) {
/* A nontrivial segment. */
@ -104,9 +106,8 @@ static int convexhull_2d_sorted(const float (*points)[2], const int points_num,
/* Get the indices of points with max X-coord and min|max Y-coord. */
maxmax = points_num - 1;
xmax = points[points_num - 1][0];
for (i = points_num - 2; i >= 0; i--) {
xmax = points[maxmax][0];
for (i = maxmax - 1; i >= 0; i--) {
if (points[i][0] != xmax) {
break;
}
@ -235,12 +236,12 @@ static float convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[
int points_hull_num)
{
float area_best = FLT_MAX;
float2 dvec_best = {0.0f, 1.0f}; /* Track the best angle as a unit vector, delaying `atan2`. */
float2 sincos_best = {0.0f, 1.0f}; /* Track the best angle as a unit vector, delaying `atan2`. */
for (int i = 0, i_prev = points_hull_num - 1; i < points_hull_num; i_prev = i++) {
/* 2D rotation matrix. */
float dvec_length = 0.0f;
const float2 dvec = math::normalize_and_get_length(
const float2 sincos = math::normalize_and_get_length(
float2(points_hull[i]) - float2(points_hull[i_prev]), dvec_length);
if (UNLIKELY(dvec_length == 0.0f)) {
continue;
@ -251,8 +252,8 @@ static float convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[
for (int j = 0; j < points_hull_num; j++) {
const float2 tvec = {
mul_v2_v2_cw_x(dvec, points_hull[j]),
mul_v2_v2_cw_y(dvec, points_hull[j]),
sincos_rotate_cw_x(sincos, points_hull[j]),
sincos_rotate_cw_y(sincos, points_hull[j]),
};
bounds[0].min = math::min(bounds[0].min, tvec[0]);
@ -268,11 +269,11 @@ static float convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[
if (area_test < area_best) {
area_best = area_test;
dvec_best = dvec;
sincos_best = sincos;
}
}
return (area_best != FLT_MAX) ? float(atan2(dvec_best[0], dvec_best[1])) : 0.0f;
return (area_best != FLT_MAX) ? float(atan2(sincos_best[0], sincos_best[1])) : 0.0f;
}
/** \} */
@ -289,7 +290,7 @@ static float convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[
template<int Axis, int AxisSign>
static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2],
const int points_hull_num,
const float2 &dvec,
const float2 &sincos,
int *index_p)
{
/* NOTE(@ideasman42): This could be optimized to use a search strategy
@ -306,14 +307,14 @@ static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2],
const int index_init = *index_p;
int index_best = index_init;
float value_init = (Axis == 0) ? mul_v2_v2_cw_x(dvec, points_hull[index_best]) :
mul_v2_v2_cw_y(dvec, points_hull[index_best]);
float value_init = (Axis == 0) ? sincos_rotate_cw_x(sincos, points_hull[index_best]) :
sincos_rotate_cw_y(sincos, points_hull[index_best]);
float value_best = value_init;
/* Simply scan up the array. */
for (int count = 1; count < points_hull_num; count++) {
const int index_test = (index_init + count) % points_hull_num;
const float value_test = (Axis == 0) ? mul_v2_v2_cw_x(dvec, points_hull[index_test]) :
mul_v2_v2_cw_y(dvec, points_hull[index_test]);
const float value_test = (Axis == 0) ? sincos_rotate_cw_x(sincos, points_hull[index_test]) :
sincos_rotate_cw_y(sincos, points_hull[index_test]);
if ((AxisSign == -1) ? (value_test > value_best) : (value_test < value_best)) {
break;
}
@ -328,7 +329,7 @@ static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2],
static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int points_hull_num)
{
float area_best = FLT_MAX;
float2 dvec_best; /* Track the best angle as a unit vector, delaying `atan2`. */
float2 sincos_best; /* Track the best angle as a unit vector, delaying `atan2`. */
bool is_first = true;
/* Initialize to zero because the first pass uses the first index to set the bounds. */
@ -337,7 +338,7 @@ static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int poin
for (int i = 0, i_prev = points_hull_num - 1; i < points_hull_num; i_prev = i++) {
/* 2D rotation matrix. */
float dvec_length = 0.0f;
const float2 dvec = math::normalize_and_get_length(
const float2 sincos = math::normalize_and_get_length(
float2(points_hull[i]) - float2(points_hull[i_prev]), dvec_length);
if (UNLIKELY(dvec_length == 0.0f)) {
continue;
@ -348,16 +349,16 @@ static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int poin
blender::Bounds<float> bounds[2];
bounds[0].min = bounds[0].max = mul_v2_v2_cw_x(dvec, points_hull[0]);
bounds[1].min = bounds[1].max = mul_v2_v2_cw_y(dvec, points_hull[0]);
bounds[0].min = bounds[0].max = sincos_rotate_cw_x(sincos, points_hull[0]);
bounds[1].min = bounds[1].max = sincos_rotate_cw_y(sincos, points_hull[0]);
bounds_index[0].min = bounds_index[0].max = 0;
bounds_index[1].min = bounds_index[1].max = 0;
for (int j = 1; j < points_hull_num; j++) {
const float2 tvec = {
mul_v2_v2_cw_x(dvec, points_hull[j]),
mul_v2_v2_cw_y(dvec, points_hull[j]),
sincos_rotate_cw_x(sincos, points_hull[j]),
sincos_rotate_cw_y(sincos, points_hull[j]),
};
for (int axis = 0; axis < 2; axis++) {
if (tvec[axis] < bounds[axis].min) {
@ -372,20 +373,20 @@ static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int poin
}
area_best = (bounds[0].max - bounds[0].min) * (bounds[1].max - bounds[1].min);
dvec_best = dvec;
sincos_best = sincos;
continue;
}
/* Step the calipers to the new rotation `dvec`, returning the bounds at the same time. */
/* Step the calipers to the new rotation `sincos`, returning the bounds at the same time. */
blender::Bounds<float> bounds_test[2] = {
{convexhull_2d_compute_extent_on_axis<0, -1>(
points_hull, points_hull_num, dvec, &bounds_index[0].min),
points_hull, points_hull_num, sincos, &bounds_index[0].min),
convexhull_2d_compute_extent_on_axis<0, 1>(
points_hull, points_hull_num, dvec, &bounds_index[0].max)},
points_hull, points_hull_num, sincos, &bounds_index[0].max)},
{convexhull_2d_compute_extent_on_axis<1, -1>(
points_hull, points_hull_num, dvec, &bounds_index[1].min),
points_hull, points_hull_num, sincos, &bounds_index[1].min),
convexhull_2d_compute_extent_on_axis<1, 1>(
points_hull, points_hull_num, dvec, &bounds_index[1].max)},
points_hull, points_hull_num, sincos, &bounds_index[1].max)},
};
@ -394,11 +395,11 @@ static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int poin
if (area_test < area_best) {
area_best = area_test;
dvec_best = dvec;
sincos_best = sincos;
}
}
const float angle = (area_best != FLT_MAX) ? float(atan2(dvec_best[0], dvec_best[1])) : 0.0f;
const float angle = (area_best != FLT_MAX) ? float(atan2(sincos_best[0], sincos_best[1])) : 0.0f;
#ifdef USE_BRUTE_FORCE_ASSERT
/* Ensure the optimized result matches the brute-force version. */

View File

@ -59,7 +59,6 @@ set(SRC
set(LIB
PRIVATE bf::animrig
PRIVATE bf::blenfont
bf_blenkernel
PRIVATE bf::blenlib
PRIVATE bf::depsgraph
@ -118,6 +117,7 @@ if(WITH_GTESTS)
)
set(TEST_UTIL_LIB
${LIB}
PRIVATE bf::blenfont
bf_blenloader
)
blender_add_lib(bf_blenloader_test_util "${TEST_UTIL_SRC}" "${TEST_UTIL_INC}" "${TEST_UTIL_INC_SYS}" "${TEST_UTIL_LIB}")

View File

@ -31,10 +31,7 @@ void Profiler::add_operation_execution_time(const NodeOperation &operation,
void Profiler::add_execution_time(const bNodeInstanceKey key,
const timeit::Nanoseconds &execution_time)
{
data_.per_node_execution_time.add_or_modify(
key,
[&](timeit::Nanoseconds *total_execution_time) { *total_execution_time = execution_time; },
[&](timeit::Nanoseconds *total_execution_time) { *total_execution_time += execution_time; });
data_.per_node_execution_time.lookup_or_add(key, timeit::Nanoseconds(0)) += execution_time;
}
void Profiler::finalize(const bNodeTree &node_tree)

View File

@ -147,6 +147,19 @@ static void add_meta_data_for_input(realtime_compositor::FileOutput &file_output
void FileOutputOperation::deinit_execution()
{
/* It is possible that none of the inputs would have an image connected, which will materialize
* as a size of zero, so check this here and return early doing nothing. Just make sure to free
* the allocated buffers. */
const int2 size = int2(get_width(), get_height());
if (size == int2(0)) {
for (const FileOutputInput &input : file_output_inputs_) {
if (input.output_buffer) {
MEM_freeN(input.output_buffer);
}
}
return;
}
if (is_multi_layer()) {
execute_multi_layer();
}

View File

@ -134,51 +134,34 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const int op_width = this->get_width();
const int op_height = this->get_height();
const float center_x = op_width / 2;
const float center_y = op_height / 2;
TexResult tex_result = {0};
float vec[3];
const int thread_id = WorkScheduler::current_thread_id();
const float3 offset = inputs[0]->get_elem(0, 0);
const float3 scale = inputs[1]->get_elem(0, 0);
const int2 size = int2(this->get_width(), this->get_height());
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *tex_offset = it.in(0);
const float *tex_size = it.in(1);
float u = (it.x - center_x) / op_width * 2;
float v = (it.y - center_y) / op_height * 2;
/* Compute the coordinates in the [-1, 1] range and add 0.5 to evaluate the texture at the
* center of pixels in case it was interpolated. */
const float2 pixel_coordinates = ((float2(it.x, it.y) + 0.5f) / float2(size)) * 2.0f - 1.0f;
/* Note that it is expected that the offset is scaled by the scale. */
const float3 coordinates = (float3(pixel_coordinates, 0.0f) + offset) * scale;
/* When no interpolation/filtering happens in multitex() force nearest interpolation.
* We do it here because (a) we can't easily say multitex() that we want nearest
* interpolation and (b) in such configuration multitex() simply floor's the value
* which often produces artifacts.
*/
if (texture_ != nullptr && (texture_->imaflag & TEX_INTERPOL) == 0) {
u += 0.5f / center_x;
v += 0.5f / center_y;
}
vec[0] = tex_size[0] * (u + tex_offset[0]);
vec[1] = tex_size[1] * (v + tex_offset[1]);
vec[2] = tex_size[2] * tex_offset[2];
const int retval = multitex_ext(texture_,
vec,
nullptr,
nullptr,
0,
&tex_result,
thread_id,
pool_,
scene_color_manage_,
false);
it.out[3] = tex_result.talpha ? tex_result.trgba[3] : tex_result.tin;
if (retval & TEX_RGB) {
copy_v3_v3(it.out, tex_result.trgba);
}
else {
it.out[0] = it.out[1] = it.out[2] = it.out[3];
TexResult texture_result;
const int result_type = multitex_ext(texture_,
coordinates,
nullptr,
nullptr,
0,
&texture_result,
WorkScheduler::current_thread_id(),
pool_,
scene_color_manage_,
false);
float4 color = float4(texture_result.trgba);
color.w = texture_result.talpha ? color.w : texture_result.tin;
if (!(result_type & TEX_RGB)) {
copy_v3_fl(color, color.w);
}
copy_v4_v4(it.out, color);
}
}

View File

@ -117,6 +117,18 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
return;
}
/* Some compositor operations produce an empty output buffer by specifying a COM_AREA_NONE canvas
* to indicate an invalid output, for instance, when the Mask operation reference an invalid
* mask. The intention is that this buffer would signal that a fallback value would fill the
* canvas of consumer operations. Since the aforementioned filling is achieved through the
* Translate operation as part of canvas conversion in COM_convert_canvas, we handle the empty
* buffer case here and fill the output using a fallback black color. */
if (BLI_rcti_is_empty(&input->get_rect())) {
const float value[4] = {0.0f, 0.0f, 0.0f, 1.0f};
output->fill(area, value);
return;
}
const int delta_x = this->get_delta_x();
const int delta_y = this->get_delta_y();
for (int y = area.ymin; y < area.ymax; y++) {

View File

@ -112,6 +112,10 @@ class Context {
* render pipeline. */
virtual RenderContext *render_context() const;
/* Returns true if the compositor evaluation is canceled and that the evaluator should stop
* executing as soon as possible. */
virtual bool is_canceled() const;
/* Get the size of the compositing region. See get_compositing_region(). The output size is
* sanitized such that it is at least 1 in both dimensions. However, the developer is expected to
* gracefully handled zero sizes regions by checking the is_valid_compositing_region method. */

View File

@ -5,10 +5,13 @@
#include "BLI_math_vector.hh"
#include "BLI_rect.h"
#include "DNA_node_types.h"
#include "DNA_vec_types.h"
#include "GPU_shader.h"
#include "BKE_node_runtime.hh"
#include "COM_context.hh"
#include "COM_render_context.hh"
#include "COM_static_cache_manager.hh"
@ -23,6 +26,14 @@ RenderContext *Context::render_context() const
return nullptr;
}
bool Context::is_canceled() const
{
if (!this->get_node_tree().runtime->test_break) {
return false;
}
return this->get_node_tree().runtime->test_break(get_node_tree().runtime->tbh);
}
int2 Context::get_compositing_region_size() const
{
const rcti compositing_region = get_compositing_region();

View File

@ -4028,6 +4028,12 @@ static bool select_anim_channel_keys(bAnimContext *ac, int channel_index, bool e
return false;
}
/* Only FCuves can have their keys selected. */
if (ale->datatype != ALE_FCURVE) {
ANIM_animdata_freelist(&anim_data);
return false;
}
fcu = (FCurve *)ale->key_data;
success = (fcu != nullptr);

View File

@ -8,6 +8,7 @@
#include "BKE_context.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_report.hh"
#include "DEG_depsgraph.hh"
@ -372,7 +373,7 @@ static void GREASE_PENCIL_OT_layer_reveal(wmOperatorType *ot)
static int grease_pencil_layer_isolate_exec(bContext *C, wmOperator *op)
{
using namespace ::blender::bke::greasepencil;
using namespace blender::bke::greasepencil;
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
const int affect_visibility = RNA_boolean_get(op->ptr, "affect_visibility");
@ -469,6 +470,60 @@ static void GREASE_PENCIL_OT_layer_lock_all(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna, "lock", true, "Lock Value", "Lock/Unlock all layers");
}
static int grease_pencil_layer_duplicate_exec(bContext *C, wmOperator *op)
{
using namespace ::blender::bke::greasepencil;
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
const bool empty_keyframes = RNA_boolean_get(op->ptr, "empty_keyframes");
if (!grease_pencil.has_active_layer()) {
BKE_reportf(op->reports, RPT_ERROR, "No active layer to duplicate");
return OPERATOR_CANCELLED;
}
Layer &active_layer = *grease_pencil.get_active_layer();
Layer &new_layer = grease_pencil.add_layer(active_layer.name());
for (auto [key, frame] : active_layer.frames().items()) {
const int duration = frame.is_implicit_hold() ? 0 : active_layer.get_frame_duration_at(key);
const int drawing_index = grease_pencil.drawings().size();
GreasePencilFrame *new_frame = new_layer.add_frame(key, drawing_index, duration);
new_frame->type = frame.type;
if (empty_keyframes) {
grease_pencil.add_empty_drawings(1);
}
else {
const Drawing &drawing = *grease_pencil.get_drawing_at(active_layer, key);
grease_pencil.add_duplicate_drawings(1, drawing);
}
}
grease_pencil.move_node_after(new_layer.as_node(), active_layer.as_node());
grease_pencil.set_active_layer(&new_layer);
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
static void GREASE_PENCIL_OT_layer_duplicate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Layer";
ot->idname = "GREASE_PENCIL_OT_layer_duplicate";
ot->description = "Make a copy of the active Grease Pencil layer";
/* callbacks */
ot->exec = grease_pencil_layer_duplicate_exec;
ot->poll = active_grease_pencil_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_boolean(ot->srna, "empty_keyframes", false, "Empty Keyframes", "Add Empty Keyframes");
}
} // namespace blender::ed::greasepencil
void ED_operatortypes_grease_pencil_layers()
@ -482,6 +537,7 @@ void ED_operatortypes_grease_pencil_layers()
WM_operatortype_append(GREASE_PENCIL_OT_layer_reveal);
WM_operatortype_append(GREASE_PENCIL_OT_layer_isolate);
WM_operatortype_append(GREASE_PENCIL_OT_layer_lock_all);
WM_operatortype_append(GREASE_PENCIL_OT_layer_duplicate);
WM_operatortype_append(GREASE_PENCIL_OT_layer_group_add);
}

View File

@ -69,17 +69,19 @@ class StepDrawingGeometryBase {
/* Index of this drawing in the original combined array of all drawings in GreasePencil ID. */
int index_;
/* Data from #GreasePencilDrawingBase that needs to be saved in udo steps. */
/* Data from #GreasePencilDrawingBase that needs to be saved in undo steps. */
uint32_t flag_;
protected:
/* Ensures that the drawing from the given array at the current index exists, and has the propoer
* type.
/**
* Ensures that the drawing from the given array at the current index exists,
* and has the proposer type.
*
* Non-existing drawings can happen after extenting the drawings array.
* Non-existing drawings can happen after extending the drawings array.
*
* Mismatch in drawing types can happen when some drawings have been deleted between the undo
* step storage, and the current state of the GreasePencil data. */
* step storage, and the current state of the GreasePencil data.
*/
void decode_valid_drawingtype_at_index_ensure(MutableSpan<GreasePencilDrawingBase *> &drawings,
const GreasePencilDrawingType drawing_type) const
{

View File

@ -1875,10 +1875,6 @@ void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr);
*/
void UI_but_drag_set_path(uiBut *but, const char *path);
void UI_but_drag_set_name(uiBut *but, const char *name);
/**
* Value from button itself.
*/
void UI_but_drag_set_value(uiBut *but);
/**
* Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged.

View File

@ -78,11 +78,6 @@ void UI_but_drag_set_name(uiBut *but, const char *name)
but->dragpoin = (void *)name;
}
void UI_but_drag_set_value(uiBut *but)
{
but->dragtype = WM_DRAG_VALUE;
}
void UI_but_drag_set_image(uiBut *but, const char *path, int icon, const ImBuf *imb, float scale)
{
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
@ -108,7 +103,6 @@ void ui_but_drag_start(bContext *C, uiBut *but)
but->icon,
but->dragtype,
but->dragpoin,
ui_but_value_get(but),
(but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA :
WM_DRAG_NOP);
/* wmDrag has ownership over dragpoin now, stop messing with it. */
@ -123,6 +117,6 @@ void ui_but_drag_start(bContext *C, uiBut *but)
/* Special feature for assets: We add another drag item that supports multiple assets. It
* gets the assets from context. */
if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) {
WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, nullptr, 0, WM_DRAG_NOP);
WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, nullptr, WM_DRAG_NOP);
}
}

View File

@ -414,8 +414,6 @@ struct uiHandleButtonData {
/* coords are Window/uiBlock relative (depends on the button) */
int draglastx, draglasty;
int dragstartx, dragstarty;
int draglastvalue;
int dragstartvalue;
bool dragchange, draglock;
int dragsel;
float dragf, dragfstart;
@ -435,7 +433,6 @@ struct uiHandleButtonData {
/* Menu open, see: #UI_screen_free_active_but_highlight. */
uiPopupBlockHandle *menu;
int menuretval;
/* Search box see: #UI_screen_free_active_but_highlight. */
ARegion *searchbox;
@ -2174,7 +2171,7 @@ static bool ui_but_drag_init(bContext *C,
}
if (valid) {
WM_event_start_drag(C, ICON_COLOR, WM_DRAG_COLOR, drag_info, 0.0, WM_DRAG_FREE_DATA);
WM_event_start_drag(C, ICON_COLOR, WM_DRAG_COLOR, drag_info, WM_DRAG_FREE_DATA);
}
else {
MEM_freeN(drag_info);

View File

@ -1167,8 +1167,9 @@ static void panel_draw_aligned_backdrop(const ARegion *region,
const rcti *rect,
const rcti *header_rect)
{
const bool is_subpanel = panel->type->parent != nullptr;
const bool is_open = !UI_panel_is_closed(panel);
const bool is_subpanel = panel->type->parent != nullptr;
const bool has_header = (panel->type->flag & PANEL_TYPE_NO_HEADER) == 0;
if (is_subpanel && !is_open) {
return;
@ -1182,10 +1183,15 @@ static void panel_draw_aligned_backdrop(const ARegion *region,
GPU_blend(GPU_BLEND_ALPHA);
/* Panel backdrop. */
if (is_open || panel->type->flag & PANEL_TYPE_NO_HEADER) {
if (is_open || !has_header) {
float panel_backcolor[4];
UI_draw_roundbox_corner_set(is_open ? UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT : UI_CNR_ALL);
UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor);
if (!has_header) {
UI_GetThemeColor4fv(TH_BACK, panel_backcolor);
}
else {
UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor);
}
rctf box_rect;
box_rect.xmin = rect->xmin;
@ -1222,7 +1228,7 @@ static void panel_draw_aligned_backdrop(const ARegion *region,
}
/* Panel header backdrops for non sub-panels. */
if (!is_subpanel) {
if (!is_subpanel && has_header) {
float panel_headercolor[4];
UI_GetThemeColor4fv(UI_panel_matches_search_filter(panel) ? TH_MATCH : TH_PANEL_HEADER,
panel_headercolor);
@ -1259,7 +1265,7 @@ void ui_draw_aligned_panel(const ARegion *region,
rect->ymax + int(floor(PNL_HEADER / block->aspect + 0.001f)),
};
if (show_background) {
if (show_background || (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
panel_draw_aligned_backdrop(region, panel, rect, &header_rect);
}

View File

@ -381,7 +381,6 @@ class ViewItemAPIWrapper {
ICON_NONE,
drag_controller->get_drag_type(),
drag_controller->create_drag_data(),
0,
WM_DRAG_FREE_DATA);
drag_controller->on_drag_start();

View File

@ -3222,8 +3222,6 @@ static int object_convert_exec(bContext *C, wmOperator *op)
{
ob->flag |= OB_DONE;
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
if (keep_original) {
BLI_assert_unreachable();
}
@ -3231,16 +3229,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
newob = ob;
}
GreasePencil *new_grease_pencil = static_cast<GreasePencil *>(
BKE_id_new(bmain, ID_GP, newob->id.name + 2));
newob->data = new_grease_pencil;
newob->type = OB_GREASE_PENCIL;
bke::greasepencil::convert::legacy_gpencil_to_grease_pencil(
*bmain, *new_grease_pencil, *gpd);
BKE_object_free_derived_caches(newob);
BKE_object_free_modifiers(newob, 0);
bke::greasepencil::convert::legacy_gpencil_object(*bmain, *newob);
}
else if (target == OB_CURVES) {
ob->flag |= OB_DONE;

View File

@ -150,12 +150,12 @@ static void console_cursor(wmWindow *win, ScrArea * /*area*/, ARegion *region)
/* ************* dropboxes ************* */
static bool id_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
static bool console_drop_id_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return WM_drag_get_local_ID(drag, 0) != nullptr;
}
static void id_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
static void console_drop_id_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
ID *id = WM_drag_get_local_ID(drag, 0);
@ -164,25 +164,47 @@ static void id_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
RNA_string_set(drop->ptr, "text", text.c_str());
}
static bool path_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
static bool console_drop_path_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return (drag->type == WM_DRAG_PATH);
}
static void path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
static void console_drop_path_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
char pathname[FILE_MAX + 2];
SNPRINTF(pathname, "\"%s\"", WM_drag_get_single_path(drag));
RNA_string_set(drop->ptr, "text", pathname);
}
static bool console_drop_string_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return (drag->type == WM_DRAG_STRING);
}
static void console_drop_string_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
/* NOTE(@ideasman42): Only a single line is supported, multiple lines could be supported
* but this implies executing all lines except for the last. While we could consider that,
* there are some security implications for this, so just drop one line for now. */
std::string str = WM_drag_get_string_firstline(drag);
RNA_string_set(drop->ptr, "text", str.c_str());
}
/* this region dropbox definition */
static void console_dropboxes()
{
ListBase *lb = WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy, nullptr, nullptr);
WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, nullptr, nullptr);
WM_dropbox_add(
lb, "CONSOLE_OT_insert", console_drop_id_poll, console_drop_id_copy, nullptr, nullptr);
WM_dropbox_add(
lb, "CONSOLE_OT_insert", console_drop_path_poll, console_drop_path_copy, nullptr, nullptr);
WM_dropbox_add(lb,
"CONSOLE_OT_insert",
console_drop_string_poll,
console_drop_string_copy,
nullptr,
nullptr);
}
/* ************* end drop *********** */

View File

@ -577,13 +577,15 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)
PointerRNA *props;
UI_but_extra_operator_icon_add((uiBut *)this->view_item_button(),
UI_but_extra_operator_icon_add(reinterpret_cast<uiBut *>(this->view_item_button()),
"ASSET_OT_catalogs_save",
WM_OP_INVOKE_DEFAULT,
ICON_FILE_TICK);
props = UI_but_extra_operator_icon_add(
(uiBut *)this->view_item_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
props = UI_but_extra_operator_icon_add(reinterpret_cast<uiBut *>(this->view_item_button()),
"ASSET_OT_catalog_new",
WM_OP_INVOKE_DEFAULT,
ICON_ADD);
/* No parent path to use the root level. */
RNA_string_set(props, "parent_path", nullptr);
}

View File

@ -1453,7 +1453,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, wmOperator * /*op*/, cons
TSE_GPENCIL_EFFECT_BASE);
const eWM_DragDataType wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID;
wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP);
wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, WM_DRAG_NOP);
if (use_datastack_drag) {
TreeElement *te_bone = nullptr;

View File

@ -301,7 +301,7 @@ static void text_cursor(wmWindow *win, ScrArea *area, ARegion *region)
/* ************* dropboxes ************* */
static bool text_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
static bool text_drop_path_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
if (drag->type == WM_DRAG_PATH) {
const eFileSel_File_Types file_type = eFileSel_File_Types(WM_drag_get_path_file_type(drag));
@ -312,18 +312,18 @@ static bool text_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*eve
return false;
}
static void text_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
static void text_drop_path_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
/* copy drag path to properties */
RNA_string_set(drop->ptr, "filepath", WM_drag_get_single_path(drag));
}
static bool text_drop_paste_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
static bool text_drop_id_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return (drag->type == WM_DRAG_ID);
}
static void text_drop_paste(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
static void text_drop_id_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
ID *id = WM_drag_get_local_ID(drag, 0);
@ -332,13 +332,26 @@ static void text_drop_paste(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
RNA_string_set(drop->ptr, "text", text.c_str());
}
static bool text_drop_string_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return (drag->type == WM_DRAG_STRING);
}
static void text_drop_string_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
const std::string &str = WM_drag_get_string(drag);
RNA_string_set(drop->ptr, "text", str.c_str());
}
/* this region dropbox definition */
static void text_dropboxes()
{
ListBase *lb = WM_dropboxmap_find("Text", SPACE_TEXT, RGN_TYPE_WINDOW);
WM_dropbox_add(lb, "TEXT_OT_open", text_drop_poll, text_drop_copy, nullptr, nullptr);
WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_paste_poll, text_drop_paste, nullptr, nullptr);
WM_dropbox_add(lb, "TEXT_OT_open", text_drop_path_poll, text_drop_path_copy, nullptr, nullptr);
WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_id_poll, text_drop_id_copy, nullptr, nullptr);
WM_dropbox_add(
lb, "TEXT_OT_insert", text_drop_string_poll, text_drop_string_copy, nullptr, nullptr);
}
/* ************* end drop *********** */

View File

@ -335,7 +335,12 @@ static void TimeToTransData(
copy_v3_v3(td->iloc, td->loc);
td->val = time;
td->ival = *(time);
td->center[0] = td->ival;
if (adt) {
td->center[0] = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP);
}
else {
td->center[0] = td->ival;
}
td->center[1] = ypos;
/* Store the AnimData where this keyframe exists as a keyframe of the

View File

@ -37,7 +37,7 @@ static void extend_curves_straight(const float used_percent_length,
float overshoot_point_param = used_percent_length * (new_size - 1);
if (start_points[curve]) {
/** Here we use the vector between two adjacent points around #overshoot_point_param as
* our reference forthe direction of extention, however to have better tolerance for jitter,
* our reference for the direction of extension, however to have better tolerance for jitter,
* using the vector (a_few_points_back - end_point) might be a better solution in the future.
*/
int index1 = math::floor(overshoot_point_param);
@ -271,7 +271,7 @@ bke::CurvesGeometry extend_curves(bke::CurvesGeometry &src_curves,
OffsetIndices dst_indices = offset_indices::accumulate_counts_to_offsets(dst_points_by_curve);
int target_point_count = dst_points_by_curve.last();
/* Make dest to source map for points. */
/* Make destination to source map for points. */
Array<int> dst_to_src_point(target_point_count);
for (const int curve : src_curves.curves_range()) {
const int point_count = points_by_curve[curve].size();

View File

@ -333,13 +333,13 @@ typedef enum {
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
/**
* Compile all staticly defined shaders and print a report to the console.
* Compile all statically defined shaders and print a report to the console.
*
* This is used for platform support, where bug reports can list all failing shaders.
*/
void GPU_shader_compile_static();
/** DEPRECATED: Use hardcoded buffer location instead. */
/** DEPRECATED: Use hard-coded buffer location instead. */
typedef enum {
GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */
GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */
@ -353,7 +353,7 @@ typedef enum {
GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
} GPUUniformBlockBuiltin;
/** DEPRECATED: Use hardcoded buffer location instead. */
/** DEPRECATED: Use hard-coded buffer location instead. */
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin);
/** DEPRECATED: Kept only because of Python GPU API. */

View File

@ -577,29 +577,26 @@ void GPU_shader_constant_int_ex(GPUShader *sh, int location, int value)
{
Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::INT);
shader.constants.values[location].i = value;
shader.constants.is_dirty = true;
shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].i, value);
}
void GPU_shader_constant_uint_ex(GPUShader *sh, int location, uint value)
{
Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::UINT);
shader.constants.values[location].u = value;
shader.constants.is_dirty = true;
shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].u, value);
}
void GPU_shader_constant_float_ex(GPUShader *sh, int location, float value)
{
Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::FLOAT);
shader.constants.values[location].f = value;
shader.constants.is_dirty = true;
shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].f, value);
}
void GPU_shader_constant_bool_ex(GPUShader *sh, int location, bool value)
{
Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::BOOL);
shader.constants.values[location].u = value;
shader.constants.is_dirty = true;
shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].u,
static_cast<uint32_t>(value));
}
void GPU_shader_constant_int(GPUShader *sh, const char *name, int value)

View File

@ -9,11 +9,10 @@
#include <boost/python/call_method.hpp>
#include <boost/python/class.hpp>
#include <boost/python/import.hpp>
#include <boost/python/object.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/to_python_converter.hpp>
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "BKE_report.hh"
@ -22,49 +21,52 @@
#include "RNA_types.hh"
#include "bpy_rna.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include <list>
#include <memory>
using namespace boost;
namespace blender::io::usd {
using USDHookList = std::list<USDHook *>;
using USDHookList = std::list<std::unique_ptr<USDHook>>;
/* USD hook type declarations */
static USDHookList g_usd_hooks;
void USD_register_hook(USDHook *hook)
static USDHookList &hook_list()
{
if (std::find(g_usd_hooks.begin(), g_usd_hooks.end(), hook) != g_usd_hooks.end()) {
static USDHookList hooks{};
return hooks;
}
void USD_register_hook(std::unique_ptr<USDHook> hook)
{
if (USD_find_hook_name(hook->idname)) {
/* The hook is already in the list. */
return;
}
/* Add hook type to the list. */
g_usd_hooks.push_back(hook);
hook_list().push_back(std::move(hook));
}
void USD_unregister_hook(USDHook *hook)
{
g_usd_hooks.remove(hook);
hook_list().remove_if(
[hook](const std::unique_ptr<USDHook> &item) { return item.get() == hook; });
}
USDHook *USD_find_hook_name(const char name[])
USDHook *USD_find_hook_name(const char idname[])
{
/* sanity checks */
if (g_usd_hooks.empty() || (name == nullptr) || (name[0] == 0)) {
if (hook_list().empty() || (idname == nullptr) || (idname[0] == 0)) {
return nullptr;
}
USDHookList::iterator hook_iter = std::find_if(
g_usd_hooks.begin(), g_usd_hooks.end(), [name](USDHook *hook) {
return STREQ(hook->idname, name);
hook_list().begin(), hook_list().end(), [idname](const std::unique_ptr<USDHook> &item) {
return STREQ(item->idname, idname);
});
return (hook_iter == g_usd_hooks.end()) ? nullptr : *hook_iter;
return (hook_iter == hook_list().end()) ? nullptr : hook_iter->get();
}
/* Convert PointerRNA to a PyObject*. */
@ -136,7 +138,7 @@ void register_hook_converters()
static bool registered = false;
/* No need to register if there are no hooks. */
if (g_usd_hooks.empty()) {
if (hook_list().empty()) {
return;
}
@ -196,22 +198,22 @@ class USDHookInvoker {
/* Attempt to call the function, if defined by the registered hooks. */
void call() const
{
if (g_usd_hooks.empty()) {
if (hook_list().empty()) {
return;
}
PyGILState_STATE gilstate = PyGILState_Ensure();
/* Iterate over the hooks and invoke the hook function, if it's defined. */
USDHookList::const_iterator hook_iter = g_usd_hooks.begin();
while (hook_iter != g_usd_hooks.end()) {
USDHookList::const_iterator hook_iter = hook_list().begin();
while (hook_iter != hook_list().end()) {
/* XXX: Not sure if this is necessary:
* Advance the iterator before invoking the callback, to guard
* against the unlikely error where the hook is de-registered in
* the callback. This would prevent a crash due to the iterator
* getting invalidated. */
USDHook *hook = *hook_iter;
USDHook *hook = hook_iter->get();
++hook_iter;
if (!hook->rna_ext.data) {
@ -329,7 +331,7 @@ class OnImportInvoker : public USDHookInvoker {
void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports)
{
if (g_usd_hooks.empty()) {
if (hook_list().empty()) {
return;
}
@ -342,7 +344,7 @@ void call_material_export_hooks(pxr::UsdStageRefPtr stage,
pxr::UsdShadeMaterial &usd_material,
ReportList *reports)
{
if (g_usd_hooks.empty()) {
if (hook_list().empty()) {
return;
}
@ -352,7 +354,7 @@ void call_material_export_hooks(pxr::UsdStageRefPtr stage,
void call_import_hooks(pxr::UsdStageRefPtr stage, ReportList *reports)
{
if (g_usd_hooks.empty()) {
if (hook_list().empty()) {
return;
}

View File

@ -204,13 +204,12 @@ struct USDHook {
ExtensionRNA rna_ext;
};
void USD_register_hook(USDHook *hook);
void USD_register_hook(std::unique_ptr<USDHook> hook);
/**
* Remove the given entry from the list of registered hooks.
* Note that this does not free the allocated memory for the
* hook instance, so a separate call to `MEM_freeN(hook)` is required.
* Remove the given entry from the list of registered hooks and
* free the allocated memory for the hook instance.
*/
void USD_unregister_hook(USDHook *hook);
USDHook *USD_find_hook_name(const char name[]);
USDHook *USD_find_hook_name(const char idname[]);
}; // namespace blender::io::usd

View File

@ -923,4 +923,24 @@
.step = 4,\
}
#define _DNA_DEFAULT_GreasePencilWeightAngleModifierData \
{ \
.flag = 0, \
.axis = 1, \
}
#define _DNA_DEFAULT_GreasePencilArrayModifierData \
{ \
.object = NULL, \
.count = 2, \
.flag = GP_ARRAY_USE_RELATIVE, \
.offset = {0.0f, 0.0f, 0.0f}, \
.shift = {1.0f, 0.0f, 0.0f}, \
.rnd_offset = {0.0f, 0.0f, 0.0f}, \
.rnd_rot = {0.0f, 0.0f, 0.0f}, \
.rnd_scale = {0.0f, 0.0f, 0.0f}, \
.seed = 1, \
.mat_rpl = 0, \
}
/* clang-format off */

View File

@ -108,6 +108,8 @@ typedef enum ModifierType {
eModifierType_GreasePencilDash = 71,
eModifierType_GreasePencilMultiply = 72,
eModifierType_GreasePencilLength = 73,
eModifierType_GreasePencilWeightAngle = 74,
eModifierType_GreasePencilArray = 75,
NUM_MODIFIER_TYPES,
} ModifierType;
@ -2867,3 +2869,60 @@ typedef struct GreasePencilLengthModifierData {
void *_pad1;
} GreasePencilLengthModifierData;
typedef struct GreasePencilWeightAngleModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
/** #GreasePencilWeightAngleModifierFlag */
int flag;
float min_weight;
/** Axis. */
int16_t axis;
/** #GreasePencilWeightAngleModifierSpace */
int16_t space;
/** Angle */
float angle;
/** Weights output to this vertex group, can be the same as source group. */
char target_vgname[64];
void *_pad;
} GreasePencilWeightAngleModifierData;
typedef enum GreasePencilWeightAngleModifierFlag {
MOD_GREASE_PENCIL_WEIGHT_ANGLE_MULTIPLY_DATA = (1 << 5),
MOD_GREASE_PENCIL_WEIGHT_ANGLE_INVERT_OUTPUT = (1 << 6),
} GreasePencilWeightAngleModifierFlag;
typedef enum GreasePencilWeightAngleModifierSpace {
MOD_GREASE_PENCIL_WEIGHT_ANGLE_SPACE_LOCAL = 0,
MOD_GREASE_PENCIL_WEIGHT_ANGLE_SPACE_WORLD = 1,
} GreasePencilWeightAngleModifierSpace;
typedef struct GreasePencilArrayModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
struct Object *object;
int count;
/** #GreasePencilArrayModifierFlag */
int flag;
float offset[3];
float shift[3];
float rnd_offset[3];
float rnd_rot[3];
float rnd_scale[3];
char _pad[4];
/** (first element is the index) random values. (?) */
int seed;
/* Replacement material index. */
int mat_rpl;
} GreasePencilArrayModifierData;
typedef enum GreasePencilArrayModifierFlag {
MOD_GREASE_PENCIL_ARRAY_USE_OFFSET = (1 << 7),
MOD_GREASE_PENCIL_ARRAY_USE_RELATIVE = (1 << 8),
MOD_GREASE_PENCIL_ARRAY_USE_OB_OFFSET = (1 << 9),
MOD_GREASE_PENCIL_ARRAY_UNIFORM_RANDOM_SCALE = (1 << 10),
} GreasePencilArrayModifierFlag;

View File

@ -339,6 +339,8 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilLatticeModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierSegment);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilMultiModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilWeightAngleModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilArrayModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@ -557,6 +559,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilSubdivModifierData),
SDNA_DEFAULT_DECL(GreasePencilNoiseModifierData),
SDNA_DEFAULT_DECL(GreasePencilLengthModifierData),
SDNA_DEFAULT_DECL(GreasePencilWeightAngleModifierData),
/* Grease Pencil 3.0 defaults. */
SDNA_DEFAULT_DECL(GreasePencilSmoothModifierData),
@ -600,6 +603,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilDashModifierSegment),
SDNA_DEFAULT_DECL(GreasePencilDashModifierData),
SDNA_DEFAULT_DECL(GreasePencilMultiModifierData),
SDNA_DEFAULT_DECL(GreasePencilArrayModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX

View File

@ -314,7 +314,7 @@ static void rna_def_animviz_paths(BlenderRNA *brna)
prop, "Has Motion Paths", "Are there any bone paths that will need updating (read-only)");
/* If enabled, bakes the motion paths into camera space. */
prop = RNA_def_property(srna, "bake_in_camera_space", PROP_BOOLEAN, PROP_NONE);
prop = RNA_def_property(srna, "use_camera_space_bake", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "path_bakeflag", MOTIONPATH_BAKE_CAMERA_SPACE);
RNA_def_property_ui_text(
prop,

View File

@ -403,6 +403,10 @@ static PointerRNA rna_AttributeGroup_new(
CustomDataLayer *layer = BKE_id_attribute_new(
id, name, eCustomDataType(type), AttrDomain(domain), reports);
if (!layer) {
return PointerRNA_NULL;
}
if ((GS(id->name) == ID_ME) && ELEM(layer->type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) {
Mesh *mesh = (Mesh *)id;
if (!mesh->active_color_attribute) {

View File

@ -116,6 +116,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_OPACITY,
"Opacity",
"Change the opacity of the strokes"},
{eModifierType_GreasePencilWeightAngle,
"GREASE_PENCIL_VERTEX_WEIGHT_ANGLE",
ICON_MOD_VERTEX_WEIGHT,
"Vertex Weight Angle",
"Generate vertex weights base on stroke angle"},
RNA_ENUM_ITEM_HEADING(N_("Generate"), nullptr),
{eModifierType_Array,
@ -211,6 +216,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_WIREFRAME,
"Wireframe",
"Convert faces into thickened edges"},
{eModifierType_GreasePencilArray,
"GREASE_PENCIL_ARRAY",
ICON_MOD_ARRAY,
"Array strokes",
"Duplicate strokes into an array"},
{eModifierType_GreasePencilLength,
"GREASE_PENCIL_LENGTH",
ICON_MOD_LENGTH,
@ -888,6 +898,7 @@ RNA_MOD_VGROUP_NAME_SET(WeightVGProximity, mask_defgrp_name);
RNA_MOD_VGROUP_NAME_SET(WeightedNormal, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(Weld, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(Wireframe, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(GreasePencilWeightAngle, target_vgname);
static void rna_ExplodeModifier_vgroup_get(PointerRNA *ptr, char *value)
{
@ -1882,6 +1893,8 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilLattice);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilDash);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilMulti);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilLength);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilWeightAngle);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilArray);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity);
@ -1892,6 +1905,7 @@ RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilNoise);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilThick);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLattice);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLength);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightAngle);
static void rna_GreasePencilOpacityModifier_opacity_factor_range(
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
@ -8649,6 +8663,116 @@ static void rna_def_modifier_grease_pencil_thickness(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_array(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "GreasePencilArrayModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Instance Modifier", "Create grid of duplicate instances");
RNA_def_struct_sdna(srna, "GreasePencilArrayModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_ARRAY);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilArrayModifier_material_filter_set");
rna_def_modifier_panel_open_prop(srna, "open_constant_offset_panel", 0);
rna_def_modifier_panel_open_prop(srna, "open_relative_offset_panel", 1);
rna_def_modifier_panel_open_prop(srna, "open_object_offset_panel", 2);
rna_def_modifier_panel_open_prop(srna, "open_randomize_panel", 3);
rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 4);
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "count", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 1, SHRT_MAX);
RNA_def_property_ui_range(prop, 1, 50, 1, -1);
RNA_def_property_ui_text(prop, "Count", "Number of items");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
/* Offset parameters */
prop = RNA_def_property(srna, "offset_object", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "object");
RNA_def_property_ui_text(
prop,
"Offset Object",
"Use the location and rotation of another object to determine the distance and "
"rotational change between arrayed items");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
prop = RNA_def_property(srna, "constant_offset", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_float_sdna(prop, nullptr, "offset");
RNA_def_property_ui_text(prop, "Constant Offset", "Value for the distance between items");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "relative_offset", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, nullptr, "shift");
RNA_def_property_ui_text(
prop,
"Relative Offset",
"The size of the geometry will determine the distance between arrayed items");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "random_offset", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, nullptr, "rnd_offset");
RNA_def_property_ui_text(prop, "Random Offset", "Value for changes in location");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_EULER);
RNA_def_property_float_sdna(prop, nullptr, "rnd_rot");
RNA_def_property_ui_text(prop, "Random Rotation", "Value for changes in rotation");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 100, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "random_scale", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, nullptr, "rnd_scale");
RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED);
RNA_def_property_ui_text(prop, "Seed", "Random seed");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "replace_material", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "mat_rpl");
RNA_def_property_range(prop, 0, SHRT_MAX);
RNA_def_property_ui_text(
prop,
"Material",
"Index of the material used for generated strokes (0 keep original material)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_constant_offset", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_ARRAY_USE_OFFSET);
RNA_def_property_ui_text(prop, "Offset", "Enable offset");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_object_offset", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_ARRAY_USE_OB_OFFSET);
RNA_def_property_ui_text(prop, "Use Object Offset", "Enable object offset");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_relative_offset", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_ARRAY_USE_RELATIVE);
RNA_def_property_ui_text(prop, "Shift", "Enable shift");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "flag", MOD_GREASE_PENCIL_ARRAY_UNIFORM_RANDOM_SCALE);
RNA_def_property_ui_text(
prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_lattice(BlenderRNA *brna)
{
StructRNA *srna;
@ -8794,6 +8918,85 @@ static void rna_def_modifier_grease_pencil_dash(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_weight_angle(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem axis_items[] = {
{0, "X", 0, "X", ""},
{1, "Y", 0, "Y", ""},
{2, "Z", 0, "Z", ""},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem space_items[] = {
{MOD_GREASE_PENCIL_WEIGHT_ANGLE_SPACE_LOCAL, "LOCAL", 0, "Local Space", ""},
{MOD_GREASE_PENCIL_WEIGHT_ANGLE_SPACE_WORLD, "WORLD", 0, "World Space", ""},
{0, nullptr, 0, nullptr, nullptr},
};
srna = RNA_def_struct(brna, "GreasePencilWeightAngleModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Weight Modifier Angle", "Calculate Vertex Weight dynamically");
RNA_def_struct_sdna(srna, "GreasePencilWeightAngleModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_VERTEX_WEIGHT);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilWeightAngleModifier_material_filter_set");
rna_def_modifier_grease_pencil_vertex_group(
srna, "rna_GreasePencilWeightAngleModifier_vertex_group_name_set");
rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0);
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "target_vertex_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "target_vgname");
RNA_def_property_ui_text(prop, "Vertex Group", "Output Vertex group");
RNA_def_property_string_funcs(
prop, nullptr, nullptr, "rna_GreasePencilWeightAngleModifier_target_vgname_set");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_multiply", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_WEIGHT_MULTIPLY_DATA);
RNA_def_property_ui_text(
prop,
"Multiply Weights",
"Multiply the calculated weights with the existing values in the vertex group");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_invert_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_WEIGHT_INVERT_OUTPUT);
RNA_def_property_ui_text(prop, "Invert", "Invert output weight values");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, nullptr, "angle");
RNA_def_property_ui_text(prop, "Angle", "Angle");
RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f));
RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update");
prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "axis");
RNA_def_property_enum_items(prop, axis_items);
RNA_def_property_ui_text(prop, "Axis", "");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "space");
RNA_def_property_enum_items(prop, space_items);
RNA_def_property_ui_text(prop, "Space", "Coordinates space");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "min_weight");
RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_multiply(BlenderRNA *brna)
{
StructRNA *srna;
@ -9035,6 +9238,8 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_dash(brna);
rna_def_modifier_grease_pencil_multiply(brna);
rna_def_modifier_grease_pencil_length(brna);
rna_def_modifier_grease_pencil_weight_angle(brna);
rna_def_modifier_grease_pencil_array(brna);
}
#endif

View File

@ -6422,7 +6422,7 @@ static void rna_def_space_graph(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Show Handles", "Show handles of Bézier control points");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, nullptr);
prop = RNA_def_property(srna, "autolock_translation_axis", PROP_BOOLEAN, PROP_NONE);
prop = RNA_def_property(srna, "use_auto_lock_translation_axis", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SIPO_AUTOLOCK_AXIS);
RNA_def_property_ui_text(prop,
"Auto-Lock Key Axis",

View File

@ -46,8 +46,6 @@ static bool rna_USDHook_unregister(Main * /*bmain*/, StructRNA *type)
/* unlink Blender-side data */
USD_unregister_hook(hook);
MEM_freeN(hook);
return true;
}
@ -60,8 +58,7 @@ static StructRNA *rna_USDHook_register(Main *bmain,
StructFreeFunc free)
{
const char *error_prefix = "Registering USD hook class:";
USDHook dummy_hook = {{0}};
USDHook *hook;
USDHook dummy_hook{};
/* setup dummy type info to store static properties in */
PointerRNA dummy_hook_ptr = RNA_pointer_create(nullptr, &RNA_USDHook, &dummy_hook);
@ -82,8 +79,7 @@ static StructRNA *rna_USDHook_register(Main *bmain,
}
/* check if we have registered this hook before, and remove it */
hook = USD_find_hook_name(dummy_hook.idname);
if (hook) {
if (USDHook *hook = USD_find_hook_name(dummy_hook.idname)) {
BKE_reportf(reports,
RPT_INFO,
"%s '%s', bl_idname '%s' has been registered before, unregistering previous",
@ -105,23 +101,24 @@ static StructRNA *rna_USDHook_register(Main *bmain,
}
/* create a new KeyingSetInfo type */
hook = static_cast<USDHook *>(MEM_mallocN(sizeof(USDHook), "python USD hook"));
memcpy(hook, &dummy_hook, sizeof(USDHook));
auto hook = std::make_unique<USDHook>();
*hook = dummy_hook;
/* set RNA-extensions info */
hook->rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, hook->idname, &RNA_USDHook);
hook->rna_ext.data = data;
hook->rna_ext.call = call;
hook->rna_ext.free = free;
RNA_struct_blender_type_set(hook->rna_ext.srna, hook);
RNA_struct_blender_type_set(hook->rna_ext.srna, hook.get());
/* add and register with other info as needed */
USD_register_hook(hook);
StructRNA *srna = hook->rna_ext.srna;
USD_register_hook(std::move(hook));
WM_main_add_notifier(NC_WINDOW, nullptr);
/* return the struct-rna added */
return hook->rna_ext.srna;
return srna;
}
#else

View File

@ -44,6 +44,7 @@ set(SRC
intern/MOD_edgesplit.cc
intern/MOD_explode.cc
intern/MOD_fluid.cc
intern/MOD_grease_pencil_array.cc
intern/MOD_grease_pencil_color.cc
intern/MOD_grease_pencil_dash.cc
intern/MOD_grease_pencil_lattice.cc
@ -58,6 +59,7 @@ set(SRC
intern/MOD_grease_pencil_thickness.cc
intern/MOD_grease_pencil_tint.cc
intern/MOD_grease_pencil_util.cc
intern/MOD_grease_pencil_weight_angle.cc
intern/MOD_hook.cc
intern/MOD_laplaciandeform.cc
intern/MOD_laplaciansmooth.cc

View File

@ -86,6 +86,8 @@ extern ModifierTypeInfo modifierType_GreasePencilLattice;
extern ModifierTypeInfo modifierType_GreasePencilDash;
extern ModifierTypeInfo modifierType_GreasePencilMultiply;
extern ModifierTypeInfo modifierType_GreasePencilLength;
extern ModifierTypeInfo modifierType_GreasePencilWeightAngle;
extern ModifierTypeInfo modifierType_GreasePencilArray;
/* MOD_util.cc */

View File

@ -0,0 +1,397 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "DNA_defaults.h"
#include "DNA_modifier_types.h"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "BKE_lib_query.hh"
#include "BKE_material.h"
#include "BKE_modifier.hh"
#include "BKE_screen.hh"
#include "BLO_read_write.hh"
#include "GEO_realize_instances.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "BLT_translation.hh"
#include "BLI_bounds_types.hh"
#include "BLI_hash.h"
#include "BLI_math_matrix.hh"
#include "BLI_rand.h"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "MOD_grease_pencil_util.hh"
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
namespace blender {
static void init_data(ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mmd, modifier));
MEMCPY_STRUCT_AFTER(mmd, DNA_struct_default_get(GreasePencilArrayModifierData), modifier);
modifier::greasepencil::init_influence_data(&mmd->influence, false);
}
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
{
const auto *mmd = reinterpret_cast<const GreasePencilArrayModifierData *>(md);
auto *tmmd = reinterpret_cast<GreasePencilArrayModifierData *>(target);
modifier::greasepencil::free_influence_data(&tmmd->influence);
BKE_modifier_copydata_generic(md, target, flag);
modifier::greasepencil::copy_influence_data(&mmd->influence, &tmmd->influence, flag);
}
static void free_data(ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
modifier::greasepencil::free_influence_data(&mmd->influence);
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
{
auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
walk(user_data, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
}
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
if (mmd->object != nullptr) {
DEG_add_object_relation(
ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Grease Pencil Array Modifier");
DEG_add_depends_on_transform_relation(ctx->node, "Grease Pencil Array Modifier");
}
}
static float4x4 get_array_matrix(const Object &ob,
const GreasePencilArrayModifierData &mmd,
const int elem_idx,
const bool use_object_offset)
{
if (use_object_offset) {
float4x4 mat_offset = float4x4::identity();
if (mmd.flag & MOD_GREASE_PENCIL_ARRAY_USE_OFFSET) {
mat_offset[3] += mmd.offset;
}
const float4x4 obinv = ob.world_to_object();
return mat_offset * obinv * mmd.object->object_to_world();
}
const float3 offset = [&]() {
if (mmd.flag & MOD_GREASE_PENCIL_ARRAY_USE_OFFSET) {
return float3(mmd.offset) * elem_idx;
}
return float3(0.0f);
}();
return math::from_location<float4x4>(offset);
}
static float4x4 get_rand_matrix(const GreasePencilArrayModifierData &mmd,
const Object &ob,
const int elem_id)
{
int seed = mmd.seed;
seed += BLI_hash_string(ob.id.name + 2);
seed += BLI_hash_string(mmd.modifier.name);
const float rand_offset = BLI_hash_int_01(seed);
float3x3 rand;
for (int j = 0; j < 3; j++) {
const uint3 primes(2, 3, 7);
double3 offset(0.0);
double3 r;
/* To ensure a nice distribution, we use halton sequence and offset using the seed. */
BLI_halton_3d(primes, offset, elem_id, r);
if ((mmd.flag & MOD_GREASE_PENCIL_ARRAY_UNIFORM_RANDOM_SCALE) && j == 2) {
float rand_value;
rand_value = math::mod(r[0] * 2.0 - 1.0 + rand_offset, 1.0);
rand_value = math::mod(math::sin(rand_value * 12.9898 + j * 78.233) * 43758.5453, 1.0);
rand[j] = float3(rand_value);
}
else {
for (int i = 0; i < 3; i++) {
rand[j][i] = math::mod(r[i] * 2.0 - 1.0 + rand_offset, 1.0);
rand[j][i] = math::mod(math::sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0);
}
}
}
/* Calculate Random matrix. */
return math::from_loc_rot_scale<float4x4>(
mmd.rnd_offset * rand[0], mmd.rnd_rot * rand[1], float3(1.0f) + mmd.rnd_scale * rand[2]);
};
static bke::CurvesGeometry create_array_copies(const Object &ob,
const GreasePencilArrayModifierData &mmd,
const bke::CurvesGeometry &base_curves,
bke::CurvesGeometry filtered_curves)
{
/* Assign replacement material on filterd curves so all copies can have this material when later
* when they get instanced. */
if (mmd.mat_rpl > 0) {
bke::MutableAttributeAccessor attributes = filtered_curves.attributes_for_write();
bke::SpanAttributeWriter<int> stroke_materials = attributes.lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Curve);
stroke_materials.span.fill(mmd.mat_rpl - 1);
stroke_materials.finish();
}
Curves *base_curves_id = bke::curves_new_nomain(base_curves);
Curves *filtered_curves_id = bke::curves_new_nomain(filtered_curves);
bke::GeometrySet base_geo = bke::GeometrySet::from_curves(base_curves_id);
bke::GeometrySet filtered_geo = bke::GeometrySet::from_curves(filtered_curves_id);
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
const int base_handle = instances->add_reference(bke::InstanceReference{base_geo});
const int filtered_handle = instances->add_reference(bke::InstanceReference{filtered_geo});
/* Always add untouched original curves. */
instances->add_instance(base_handle, float4x4::identity());
float3 size(0.0f);
if (mmd.flag & MOD_GREASE_PENCIL_ARRAY_USE_RELATIVE) {
std::optional<blender::Bounds<float3>> bounds = filtered_curves.bounds_min_max();
if (bounds.has_value()) {
size = bounds.value().max - bounds.value().min;
/* Need a minimum size (for flat drawings). */
size = math::max(size, float3(0.01f));
}
}
float4x4 current_offset = float4x4::identity();
for (const int elem_id : IndexRange(1, mmd.count - 1)) {
const bool use_object_offset = (mmd.flag & MOD_GREASE_PENCIL_ARRAY_USE_OB_OFFSET) &&
(mmd.object);
const float4x4 mat = get_array_matrix(ob, mmd, elem_id, use_object_offset);
if (use_object_offset) {
current_offset = current_offset * mat;
}
else {
current_offset = mat;
}
/* Apply relative offset. */
if (mmd.flag & MOD_GREASE_PENCIL_ARRAY_USE_RELATIVE) {
float3 relative = size * float3(mmd.shift);
float3 translate = relative * float3(float(elem_id));
current_offset.w += float4(translate, 1.0f);
}
current_offset *= get_rand_matrix(mmd, ob, elem_id);
instances->add_instance(filtered_handle, current_offset);
}
geometry::RealizeInstancesOptions options;
options.keep_original_ids = true;
options.realize_instance_attributes = false; /* Should this be true? */
options.propagation_info = {};
bke::GeometrySet result_geo = geometry::realize_instances(
bke::GeometrySet::from_instances(instances.release()), options);
return std::move(result_geo.get_curves_for_write()->geometry.wrap());
}
static void modify_drawing(const GreasePencilArrayModifierData &mmd,
const ModifierEvalContext &ctx,
bke::greasepencil::Drawing &drawing)
{
const bke::CurvesGeometry &src_curves = drawing.strokes();
if (src_curves.curve_num == 0) {
return;
}
IndexMaskMemory curve_mask_memory;
const IndexMask curves_mask = modifier::greasepencil::get_filtered_stroke_mask(
ctx.object, src_curves, mmd.influence, curve_mask_memory);
if (curves_mask.size() == src_curves.curve_num) {
/* Make a full copy so we can modify materials inside #create_array_copies before instancing.
*/
bke::CurvesGeometry copy = bke::CurvesGeometry(src_curves);
drawing.strokes_for_write() = create_array_copies(
*ctx.object, mmd, src_curves, std::move(copy));
}
else {
bke::CurvesGeometry masked_curves = bke::curves_copy_curve_selection(
src_curves, curves_mask, {});
drawing.strokes_for_write() = create_array_copies(
*ctx.object, mmd, src_curves, std::move(masked_curves));
}
drawing.tag_topology_changed();
}
static void modify_geometry_set(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet *geometry_set)
{
using bke::greasepencil::Drawing;
auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
if (!geometry_set->has_grease_pencil()) {
return;
}
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
const int frame = grease_pencil.runtime->eval_frame;
IndexMaskMemory mask_memory;
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
grease_pencil, mmd->influence, mask_memory);
const Vector<Drawing *> drawings = modifier::greasepencil::get_drawings_for_write(
grease_pencil, layer_mask, frame);
threading::parallel_for_each(drawings,
[&](Drawing *drawing) { modify_drawing(*mmd, *ctx, *drawing); });
}
static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "count", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "replace_material", UI_ITEM_NONE, IFACE_("Material Override"), ICON_NONE);
if (uiLayout *sub = uiLayoutPanelProp(
C, layout, ptr, "open_relative_offset_panel", "Relative Offset"))
{
uiLayoutSetPropSep(sub, true);
uiItemR(sub, ptr, "use_relative_offset", UI_ITEM_NONE, IFACE_("Enable"), ICON_NONE);
uiLayout *col = uiLayoutColumn(sub, false);
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_relative_offset"));
uiItemR(col, ptr, "relative_offset", UI_ITEM_NONE, IFACE_("Factor"), ICON_NONE);
}
if (uiLayout *sub = uiLayoutPanelProp(
C, layout, ptr, "open_constant_offset_panel", "Constant Offset"))
{
uiLayoutSetPropSep(sub, true);
uiItemR(sub, ptr, "use_constant_offset", UI_ITEM_NONE, IFACE_("Enable"), ICON_NONE);
uiLayout *col = uiLayoutColumn(sub, false);
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_constant_offset"));
uiItemR(col, ptr, "constant_offset", UI_ITEM_NONE, IFACE_("Distance"), ICON_NONE);
}
if (uiLayout *sub = uiLayoutPanelProp(
C, layout, ptr, "open_object_offset_panel", "Object Offset"))
{
uiLayoutSetPropSep(sub, true);
uiItemR(sub, ptr, "use_object_offset", UI_ITEM_NONE, IFACE_("Enable"), ICON_NONE);
uiLayout *col = uiLayoutColumn(sub, false);
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_object_offset"));
uiItemR(col, ptr, "offset_object", UI_ITEM_NONE, IFACE_("Object"), ICON_NONE);
}
if (uiLayout *sub = uiLayoutPanelProp(C, layout, ptr, "open_randomize_panel", "Randomize")) {
uiLayoutSetPropSep(sub, true);
uiItemR(sub, ptr, "random_offset", UI_ITEM_NONE, IFACE_("Offset"), ICON_NONE);
uiItemR(sub, ptr, "random_rotation", UI_ITEM_NONE, IFACE_("Rotation"), ICON_NONE);
uiItemR(sub, ptr, "random_scale", UI_ITEM_NONE, IFACE_("Scale"), ICON_NONE);
uiItemR(sub, ptr, "use_uniform_random_scale", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(sub, ptr, "seed", UI_ITEM_NONE, nullptr, ICON_NONE);
}
if (uiLayout *influence_panel = uiLayoutPanelProp(
C, layout, ptr, "open_influence_panel", "Influence"))
{
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
}
modifier_panel_end(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_GreasePencilArray, panel_draw);
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const auto *mmd = reinterpret_cast<const GreasePencilArrayModifierData *>(md);
BLO_write_struct(writer, GreasePencilArrayModifierData, mmd);
modifier::greasepencil::write_influence_data(writer, &mmd->influence);
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
modifier::greasepencil::read_influence_data(reader, &mmd->influence);
}
} // namespace blender
ModifierTypeInfo modifierType_GreasePencilArray = {
/*idname*/ "GreasePencilArrayModifier",
/*name*/ N_("Array"),
/*struct_name*/ "GreasePencilArrayModifierData",
/*struct_size*/ sizeof(GreasePencilArrayModifierData),
/*srna*/ &RNA_GreasePencilArrayModifier,
/*type*/ ModifierTypeType::Constructive,
/*flags*/ eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping,
/*icon*/ ICON_MOD_ARRAY,
/*copy_data*/ blender::copy_data,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ nullptr,
/*modify_geometry_set*/ blender::modify_geometry_set,
/*init_data*/ blender::init_data,
/*required_data_mask*/ nullptr,
/*free_data*/ blender::free_data,
/*is_disabled*/ nullptr,
/*update_depsgraph*/ blender::update_depsgraph,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ blender::foreach_ID_link,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ blender::panel_register,
/*blend_write*/ blender::blend_write,
/*blend_read*/ blender::blend_read,
};

View File

@ -196,9 +196,9 @@ static void deform_drawing(const ModifierData &md,
/* Always do the stretching first since it might depend on points which could be deleted by the
* shrink. */
if (mmd.start_fac < 0.0f || mmd.end_fac < 0.0f || needs_additional_shrinking) {
/* `trim_curves()` accepts the `end` valueas if it's sampling from the beginning of the
* curve, so we need to get the lengths of the curves and substract it from the back when the
* modifier is in Absolute mode. For convenience, we always call `trim_curves()` in LENGTH
/* #trim_curves() accepts the `end` values if it's sampling from the beginning of the
* curve, so we need to get the lengths of the curves and subtract it from the back when the
* modifier is in Absolute mode. For convenience, we always call #trim_curves() in LENGTH
* mode since the function itself will need length to be sampled anyway. */
Array<float> starts(curves.curves_num());
Array<float> ends(curves.curves_num());

View File

@ -0,0 +1,310 @@
/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "BLI_index_mask.hh"
#include "BLI_math_rotation.hh"
#include "BLI_string.h" /* For #STRNCPY. */
#include "BLT_translation.hh"
#include "BLO_read_write.hh"
#include "DNA_defaults.h"
#include "DNA_modifier_types.h"
#include "DNA_screen_types.h"
#include "RNA_access.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_query.hh"
#include "BKE_modifier.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "MOD_grease_pencil_util.hh"
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
#include "RNA_prototypes.h"
namespace blender {
static void init_data(ModifierData *md)
{
GreasePencilWeightAngleModifierData *gpmd =
reinterpret_cast<GreasePencilWeightAngleModifierData *>(md);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(GreasePencilWeightAngleModifierData), modifier);
modifier::greasepencil::init_influence_data(&gpmd->influence, false);
}
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
{
const GreasePencilWeightAngleModifierData *gmd =
reinterpret_cast<const GreasePencilWeightAngleModifierData *>(md);
GreasePencilWeightAngleModifierData *tgmd =
reinterpret_cast<GreasePencilWeightAngleModifierData *>(target);
BKE_modifier_copydata_generic(md, target, flag);
modifier::greasepencil::copy_influence_data(&gmd->influence, &tgmd->influence, flag);
}
static void free_data(ModifierData *md)
{
GreasePencilWeightAngleModifierData *mmd =
reinterpret_cast<GreasePencilWeightAngleModifierData *>(md);
modifier::greasepencil::free_influence_data(&mmd->influence);
}
static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
{
GreasePencilWeightAngleModifierData *mmd = (GreasePencilWeightAngleModifierData *)md;
return (mmd->target_vgname[0] == '\0');
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
{
GreasePencilWeightAngleModifierData *mmd =
reinterpret_cast<GreasePencilWeightAngleModifierData *>(md);
modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const GreasePencilWeightAngleModifierData *mmd =
reinterpret_cast<const GreasePencilWeightAngleModifierData *>(md);
BLO_write_struct(writer, GreasePencilWeightAngleModifierData, mmd);
modifier::greasepencil::write_influence_data(writer, &mmd->influence);
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
GreasePencilWeightAngleModifierData *mmd =
reinterpret_cast<GreasePencilWeightAngleModifierData *>(md);
modifier::greasepencil::read_influence_data(reader, &mmd->influence);
}
static int ensure_vertex_group(const StringRefNull name, ListBase &vertex_group_names)
{
int def_nr = BLI_findstringindex(
&vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
if (def_nr < 0) {
bDeformGroup *defgroup = MEM_cnew<bDeformGroup>(__func__);
STRNCPY(defgroup->name, name.c_str());
BLI_addtail(&vertex_group_names, defgroup);
def_nr = BLI_listbase_count(&vertex_group_names) - 1;
BLI_assert(def_nr >= 0);
}
return def_nr;
}
static bool target_vertex_group_available(const StringRefNull name,
const ListBase &vertex_group_names)
{
const int def_nr = BLI_findstringindex(
&vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
if (def_nr < 0) {
return false;
}
return true;
}
static void write_weights_for_drawing(const ModifierData &md,
const Object &ob,
bke::greasepencil::Drawing &drawing)
{
const auto &mmd = reinterpret_cast<const GreasePencilWeightAngleModifierData &>(md);
bke::CurvesGeometry &curves = drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
IndexMaskMemory memory;
const IndexMask strokes = modifier::greasepencil::get_filtered_stroke_mask(
&ob, curves, mmd.influence, memory);
if (strokes.is_empty()) {
return;
}
/* Make sure that the target vertex group is added to this drawing so we can write to it. */
ensure_vertex_group(mmd.target_vgname, curves.vertex_group_names);
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
bke::SpanAttributeWriter<float> dst_weights = attributes.lookup_for_write_span<float>(
mmd.target_vgname);
BLI_assert(!dst_weights.span.is_empty());
const VArray<float> input_weights = modifier::greasepencil::get_influence_vertex_weights(
curves, mmd.influence);
/* Use default Z up. */
const float3 z_up(0.0f, 0.0f, 1.0f);
float3 axis(0.0f);
axis[mmd.axis] = 1.0f;
float3 vec_ref;
/* Apply modifier rotation (sub 90 degrees for Y axis due Z-Up vector). */
const float rot_angle = mmd.angle - ((mmd.axis == 1) ? M_PI_2 : 0.0f);
rotate_normalized_v3_v3v3fl(vec_ref, z_up, axis, rot_angle);
const float3x3 obmat3x3(ob.object_to_world());
/* Apply the rotation of the object. */
if (mmd.space == MOD_GREASE_PENCIL_WEIGHT_ANGLE_SPACE_LOCAL) {
vec_ref = math::transform_point(obmat3x3, vec_ref);
}
const OffsetIndices points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
strokes.foreach_index(GrainSize(512), [&](const int stroke) {
const IndexRange points = points_by_curve[stroke];
if (points.size() == 1) {
dst_weights.span[points.start()] = 1.0f;
return;
}
for (const int point : points.drop_front(1)) {
const float3 p1 = math::transform_point(obmat3x3, positions[point]);
const float3 p2 = math::transform_point(obmat3x3, positions[point - 1]);
const float3 vec = p2 - p1;
const float angle = angle_on_axis_v3v3_v3(vec_ref, vec, axis);
float weight = 1.0f - math::sin(angle);
if (mmd.flag & MOD_GREASE_PENCIL_WEIGHT_ANGLE_INVERT_OUTPUT) {
weight = 1.0f - weight;
}
dst_weights.span[point] = (mmd.flag & MOD_GREASE_PENCIL_WEIGHT_ANGLE_MULTIPLY_DATA) ?
dst_weights.span[point] * weight :
weight;
dst_weights.span[point] = math::clamp(dst_weights.span[point], mmd.min_weight, 1.0f);
}
/* First point has the same weight as the second one. */
dst_weights.span[points[0]] = dst_weights.span[points[1]];
});
dst_weights.finish();
}
static void modify_geometry_set(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet *geometry_set)
{
const GreasePencilWeightAngleModifierData *mmd =
reinterpret_cast<GreasePencilWeightAngleModifierData *>(md);
if (!geometry_set->has_grease_pencil()) {
return;
}
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
if (!target_vertex_group_available(mmd->target_vgname, grease_pencil.vertex_group_names)) {
return;
}
const int current_frame = grease_pencil.runtime->eval_frame;
IndexMaskMemory mask_memory;
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
grease_pencil, mmd->influence, mask_memory);
const Vector<bke::greasepencil::Drawing *> drawings =
modifier::greasepencil::get_drawings_for_write(grease_pencil, layer_mask, current_frame);
threading::parallel_for_each(drawings, [&](bke::greasepencil::Drawing *drawing) {
write_weights_for_drawing(*md, *ctx->object, *drawing);
});
}
static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *row, *sub;
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
uiLayoutSetPropSep(layout, true);
row = uiLayoutRow(layout, true);
uiItemPointerR(row, ptr, "target_vertex_group", &ob_ptr, "vertex_groups", nullptr, ICON_NONE);
sub = uiLayoutRow(row, true);
bool has_output = RNA_string_length(ptr, "target_vertex_group") != 0;
uiLayoutSetPropDecorate(sub, false);
uiLayoutSetActive(sub, has_output);
uiItemR(sub, ptr, "use_invert_output", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
uiItemR(layout, ptr, "angle", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "axis", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "space", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "minimum_weight", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "use_multiply", UI_ITEM_NONE, nullptr, ICON_NONE);
if (uiLayout *influence_panel = uiLayoutPanelProp(
C, layout, ptr, "open_influence_panel", "Influence"))
{
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_vertex_group_settings(C, influence_panel, ptr);
}
modifier_panel_end(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_GreasePencilWeightAngle, panel_draw);
}
} // namespace blender
ModifierTypeInfo modifierType_GreasePencilWeightAngle = {
/*idname*/ "GreasePencilWeightAngleModifier",
/*name*/ N_("Weight Angle"),
/*struct_name*/ "GreasePencilWeightAngleModifierData",
/*struct_size*/ sizeof(GreasePencilWeightAngleModifierData),
/*srna*/ &RNA_GreasePencilWeightAngleModifier,
/*type*/ ModifierTypeType::NonGeometrical,
/*flags*/
eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping,
/*icon*/ ICON_MOD_VERTEX_WEIGHT,
/*copy_data*/ blender::copy_data,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ nullptr,
/*modify_geometry_set*/ blender::modify_geometry_set,
/*init_data*/ blender::init_data,
/*required_data_mask*/ nullptr,
/*free_data*/ blender::free_data,
/*is_disabled*/ blender::is_disabled,
/*update_depsgraph*/ nullptr,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ blender::foreach_ID_link,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ blender::panel_register,
/*blend_write*/ blender::blend_write,
/*blend_read*/ blender::blend_read,
};

View File

@ -277,5 +277,7 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(GreasePencilDash);
INIT_TYPE(GreasePencilMultiply);
INIT_TYPE(GreasePencilLength);
INIT_TYPE(GreasePencilWeightAngle);
INIT_TYPE(GreasePencilArray);
#undef INIT_TYPE
}

View File

@ -76,6 +76,17 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext * /*C*/, Point
using namespace blender::realtime_compositor;
/* A callback to cancel the filter operations by evaluating the context's is_canceled method. The
* API specifies that true indicates the filter should continue, while false indicates it should
* stop, so invert the condition. This callback can also be used to track progress using the given
* n argument, but we currently don't make use of it. See OIDNProgressMonitorFunction in the API
* for more information. */
[[maybe_unused]] static bool oidn_progress_monitor_function(void *user_ptr, double /*n*/)
{
const Context *context = static_cast<const Context *>(user_ptr);
return !context->is_canceled();
}
class DenoiseOperation : public NodeOperation {
public:
using NodeOperation::NodeOperation;
@ -108,6 +119,7 @@ class DenoiseOperation : public NodeOperation {
filter.setImage("output", color, oidn::Format::Float3, width, height, 0, pixel_stride);
filter.set("hdr", use_hdr());
filter.set("cleanAux", auxiliary_passes_are_clean());
filter.setProgressMonitorFunction(oidn_progress_monitor_function, &context());
/* If the albedo input is not a single value input, download the albedo texture, denoise it
* in-place if denoising auxiliary passes is needed, and set it to the main filter. */
@ -122,6 +134,7 @@ class DenoiseOperation : public NodeOperation {
"albedo", albedo, oidn::Format::Float3, width, height, 0, pixel_stride);
albedoFilter.setImage(
"output", albedo, oidn::Format::Float3, width, height, 0, pixel_stride);
albedoFilter.setProgressMonitorFunction(oidn_progress_monitor_function, &context());
albedoFilter.commit();
albedoFilter.execute();
}
@ -144,6 +157,7 @@ class DenoiseOperation : public NodeOperation {
"normal", normal, oidn::Format::Float3, width, height, 0, pixel_stride);
normalFilter.setImage(
"output", normal, oidn::Format::Float3, width, height, 0, pixel_stride);
normalFilter.setProgressMonitorFunction(oidn_progress_monitor_function, &context());
normalFilter.commit();
normalFilter.execute();
}

View File

@ -1373,7 +1373,7 @@ int WM_operator_flag_only_pass_through_on_press(int retval, const wmEvent *event
* Note that \a poin should be valid allocated and not on stack.
*/
void WM_event_start_drag(
bContext *C, int icon, eWM_DragDataType type, void *poin, double value, unsigned int flags);
bContext *C, int icon, eWM_DragDataType type, void *poin, unsigned int flags);
/**
* Create and fill the dragging data, but don't start dragging just yet (unlike
* #WM_event_start_drag()). Must be followed up by #WM_event_start_prepared_drag(), otherwise the
@ -1382,7 +1382,7 @@ void WM_event_start_drag(
* Note that \a poin should be valid allocated and not on stack.
*/
wmDrag *WM_drag_data_create(
bContext *C, int icon, eWM_DragDataType type, void *poin, double value, unsigned int flags);
bContext *C, int icon, eWM_DragDataType type, void *poin, unsigned int flags);
/**
* Invoke dragging using the given \a drag data.
*/
@ -1496,6 +1496,9 @@ bool WM_drag_has_path_file_type(const wmDrag *drag, int file_type);
*/
int /* #eFileSel_File_Types */ WM_drag_get_path_file_type(const wmDrag *drag);
const std::string &WM_drag_get_string(const wmDrag *drag);
std::string WM_drag_get_string_firstline(const wmDrag *drag);
/* Set OpenGL viewport and scissor */
void wmViewport(const rcti *winrct);
void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct);

View File

@ -1143,7 +1143,14 @@ enum eWM_DragDataType {
WM_DRAG_RNA,
WM_DRAG_PATH,
WM_DRAG_NAME,
WM_DRAG_VALUE,
/**
* Arbitrary text such as dragging from a text editor,
* this is also used when dragging a URL from a browser.
*
* An #std::string expected to be UTF8 encoded.
* Callers that require valid UTF8 sequences must validate the text.
*/
WM_DRAG_STRING,
WM_DRAG_COLOR,
WM_DRAG_DATASTACK,
WM_DRAG_ASSET_CATALOG,
@ -1255,7 +1262,6 @@ struct wmDrag {
int icon;
eWM_DragDataType type;
void *poin;
double value;
/** If no icon but imbuf should be drawn around cursor. */
const ImBuf *imb;

View File

@ -258,8 +258,7 @@ static void wm_dropbox_invoke(bContext *C, wmDrag *drag)
}
}
wmDrag *WM_drag_data_create(
bContext *C, int icon, eWM_DragDataType type, void *poin, double value, uint flags)
wmDrag *WM_drag_data_create(bContext *C, int icon, eWM_DragDataType type, void *poin, uint flags)
{
wmDrag *drag = MEM_new<wmDrag>(__func__);
@ -302,7 +301,6 @@ wmDrag *WM_drag_data_create(
drag->poin = poin;
break;
}
drag->value = value;
return drag;
}
@ -315,10 +313,9 @@ void WM_event_start_prepared_drag(bContext *C, wmDrag *drag)
wm_dropbox_invoke(C, drag);
}
void WM_event_start_drag(
bContext *C, int icon, eWM_DragDataType type, void *poin, double value, uint flags)
void WM_event_start_drag(bContext *C, int icon, eWM_DragDataType type, void *poin, uint flags)
{
wmDrag *drag = WM_drag_data_create(C, icon, type, poin, value, flags);
wmDrag *drag = WM_drag_data_create(C, icon, type, poin, flags);
WM_event_start_prepared_drag(C, drag);
}
@ -382,6 +379,11 @@ void WM_drag_data_free(eWM_DragDataType dragtype, void *poin)
wm_drag_free_path_data(&path_data);
break;
}
case WM_DRAG_STRING: {
std::string *str = static_cast<std::string *>(poin);
MEM_delete(str);
break;
}
default:
MEM_freeN(poin);
break;
@ -920,6 +922,24 @@ int WM_drag_get_path_file_type(const wmDrag *drag)
return path_data->file_types[0];
}
const std::string &WM_drag_get_string(const wmDrag *drag)
{
BLI_assert(drag->type == WM_DRAG_STRING);
const std::string *str = static_cast<const std::string *>(drag->poin);
return *str;
}
std::string WM_drag_get_string_firstline(const wmDrag *drag)
{
BLI_assert(drag->type == WM_DRAG_STRING);
const std::string *str = static_cast<const std::string *>(drag->poin);
const size_t str_eol = str->find('\n');
if (str_eol != std::string::npos) {
return str->substr(0, str_eol);
}
return *str;
}
/* ************** draw ***************** */
static void wm_drop_operator_draw(const blender::StringRef name, int x, int y)

View File

@ -1663,10 +1663,15 @@ static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr C
int icon = ED_file_extension_icon((char *)stra->strings[0]);
wmDragPath *path_data = WM_drag_create_path_data(
blender::Span((char **)stra->strings, stra->count));
WM_event_start_drag(C, icon, WM_DRAG_PATH, path_data, 0.0, WM_DRAG_NOP);
WM_event_start_drag(C, icon, WM_DRAG_PATH, path_data, WM_DRAG_NOP);
/* Void pointer should point to string, it makes a copy. */
}
}
else if (ddd->dataType == GHOST_kDragnDropTypeString) {
/* Drop an arbitrary string. */
std::string *str = MEM_new<std::string>(__func__, static_cast<const char *>(ddd->data));
WM_event_start_drag(C, ICON_NONE, WM_DRAG_STRING, str, WM_DRAG_FREE_DATA);
}
break;
}

View File

@ -1227,7 +1227,7 @@ static int arg_handle_debug_gpu_set(int /*argc*/, const char ** /*argv*/, void *
static const char arg_handle_debug_gpu_compile_shaders_set_doc[] =
"\n"
"\tCompile all staticly defined shaders to test platform compatibility.";
"\tCompile all statically defined shaders to test platform compatibility.";
static int arg_handle_debug_gpu_compile_shaders_set(int /*argc*/,
const char ** /*argv*/,
void * /*data*/)