WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 357 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
65 changed files with 1635 additions and 385 deletions
Showing only changes of commit 50c3c18d4e - 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

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