WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 370 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.Sdf as Sdf
import pxr.Usd as Usd import pxr.Usd as Usd
import pxr.UsdShade as UsdShade import pxr.UsdShade as UsdShade
import textwrap
class USDHookExample(bpy.types.USDHook): class USDHookExample(bpy.types.USDHook):

View File

@ -102,7 +102,8 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
char *GHOST_DropTargetX11::FileUrlDecode(const char *fileUrl) char *GHOST_DropTargetX11::FileUrlDecode(const char *fileUrl)
{ {
if (strncmp(fileUrl, "file://", 7) == 0) { 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; return nullptr;

View File

@ -12,6 +12,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include "GHOST_Debug.hh"
#include "GHOST_PathUtils.hh" #include "GHOST_PathUtils.hh"
#include "GHOST_Types.h" #include "GHOST_Types.h"
@ -24,9 +25,10 @@ using DecodeState_e = enum DecodeState_e {
STATE_CONVERTING 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; DecodeState_e state = STATE_SEARCH;
uint ascii_character; 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. */ /* 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); 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; const size_t decoded_size = strlen(buf_dst) + 1;
if (decoded_size != decoded_size_max) { if (decoded_size != decoded_size_max) {
char *buf_dst_trim = (char *)malloc(decoded_size); char *buf_dst_trim = (char *)malloc(decoded_size);

View File

@ -14,12 +14,13 @@
* \param buf_dst: Buffer for decoded URL. * \param buf_dst: Buffer for decoded URL.
* \param buf_dst_maxlen: Size of output buffer. * \param buf_dst_maxlen: Size of output buffer.
* \param buf_src: Input encoded buffer to be decoded. * \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. * A version of #GHOST_URL_decode that allocates the string & returns it.
* *
* \param buf_src: Input encoded buffer to be decoded. * \param buf_src: Input encoded buffer to be decoded.
* \return The decoded output buffer. * \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]); close(pipefd[0]);
} }
else {
*r_len = 0;
}
return buf; 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); CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive);
auto read_uris_fn = [](GWL_Seat *const seat, auto read_drop_data_fn = [](GWL_Seat *const seat,
GWL_DataOffer *data_offer, GWL_DataOffer *data_offer,
wl_surface *wl_surface_window, wl_surface *wl_surface_window,
const char *mime_receive) { const char *mime_receive) {
const uint64_t event_ms = seat->system->getMilliSeconds(); const uint64_t event_ms = seat->system->getMilliSeconds();
const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)}; 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; size_t data_buf_len = 0;
const char *data_buf = read_buffer_from_data_offer( const char *data_buf = read_buffer_from_data_offer(
data_offer, mime_receive, nullptr, false, &data_buf_len); data_offer, mime_receive, nullptr, nil_terminate, &data_buf_len);
std::string data = data_buf ? std::string(data_buf, data_buf_len) : "";
free(const_cast<char *>(data_buf));
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_finish(data_offer->wl.id);
wl_data_offer_destroy(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; delete data_offer;
data_offer = nullptr; 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) { /* Failure to receive drop data . */
const char file_proto[] = "file://"; if (mime_receive == ghost_wl_mime_text_uri) {
/* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`. const char file_proto[] = "file://";
* So support both, once `\n` is found, strip the preceding `\r` if found. */ /* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`.
const char lf = '\n'; * 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); const std::string_view data = std::string_view(data_buf, data_buf_len);
std::vector<std::string> uris; std::vector<std::string_view> uris;
size_t pos = 0; size_t pos = 0;
while (pos != std::string::npos) { while (pos != std::string::npos) {
pos = data.find(file_proto, pos); pos = data.find(file_proto, pos);
if (pos == std::string::npos) { if (pos == std::string::npos) {
break; 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; GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>(
if (UNLIKELY(end == std::string::npos)) { malloc(sizeof(GHOST_TStringArray)));
/* Note that most well behaved file managers will add a trailing newline, flist->count = int(uris.size());
* Gnome's web browser (44.3) doesn't, so support reading up until the last byte. */ flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
end = data.size(); 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') { CLOG_INFO(LOG, 2, "read_drop_data file_count=%d", flist->count);
end -= 1; ghost_dnd_type = GHOST_kDragnDropTypeFilenames;
} ghost_dnd_data = flist;
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()); 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 *>( if (ghost_dnd_type != GHOST_kDragnDropTypeUnknown) {
malloc(sizeof(GHOST_TStringArray))); GHOST_SystemWayland *const system = seat->system;
flist->count = int(uris.size()); GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_window);
flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *))); const int event_xy[2] = {WL_FIXED_TO_INT_FOR_WINDOW_V2(win, xy)};
for (size_t i = 0; i < uris.size(); i++) {
flist->strings[i] = reinterpret_cast<uint8_t *>(GHOST_URL_decode_alloc(uris[i].c_str())); 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); free(const_cast<char *>(data_buf));
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));
} }
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 /* 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. */ * leave callback (#data_device_handle_leave) will clear the value once this function starts. */
std::thread read_thread( 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(); read_thread.detach();
} }

View File

@ -43,7 +43,7 @@ class MotionPathButtonsPanel:
col.prop(mps, "frame_step", text="Step") col.prop(mps, "frame_step", text="Step")
row = col.row() 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: if bones:
op_category = "pose" op_category = "pose"

View File

@ -48,8 +48,13 @@ class GREASE_PENCIL_MT_grease_pencil_add_layer_extra(Menu):
def draw(self, context): def draw(self, context):
layout = self.layout 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.separator()
layout.operator("grease_pencil.layer_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") 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: if geometry_nodes_supported:
self.operator_modifier_add(layout, 'NODES') self.operator_modifier_add(layout, 'NODES')
layout.separator() 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") layout.menu("OBJECT_MT_modifier_add_edit")
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME', 'GREASEPENCIL'}: if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME', 'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_generate") 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_EDIT')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX') self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_PROXIMITY') 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) 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': if ob_type == 'MESH':
self.operator_modifier_add(layout, 'WIREFRAME') self.operator_modifier_add(layout, 'WIREFRAME')
if ob_type == 'GREASEPENCIL': 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_DASH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH') self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR') 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, "use_realtime_update")
layout.prop(st, "show_sliders") layout.prop(st, "show_sliders")
layout.prop(st, "use_auto_merge_keyframes") layout.prop(st, "use_auto_merge_keyframes")
layout.prop(st, "autolock_translation_axis") layout.prop(st, "use_auto_lock_translation_axis")
layout.separator() layout.separator()
if st.mode != 'DRIVERS': if st.mode != 'DRIVERS':

View File

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

View File

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

View File

@ -45,7 +45,7 @@ struct DupliObject {
float mat[4][4]; float mat[4][4];
float orco[3], uv[2]; float orco[3], uv[2];
short type; /* from Object.transflag */ short type; /* From #Object::transflag. */
char no_draw; char no_draw;
/* If this dupli object is belongs to a preview, this is non-null. */ /* If this dupli object is belongs to a preview, this is non-null. */
const blender::bke::GeometrySet *preview_base_geometry; 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, void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
const ListBase &vertex_group_names, const ListBase &vertex_group_names,
GreasePencilDrawing &r_drawing); 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 convert
} // namespace greasepencil } // namespace greasepencil

View File

@ -74,7 +74,7 @@ void BKE_ffmpeg_context_free(void *context_v);
void BKE_ffmpeg_exit(); 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 * After you're done using the context, call #BKE_ffmpeg_sws_release_context
* to release it. Internally the contexts are coming from the context * to release it. Internally the contexts are coming from the context
* pool/cache. * 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). * - various minor settings (add as needed).
*/ */
#define VALUE_SWAP(id) \
{ \
std::swap(userdef_a->id, userdef_b->id); \
}
#define DATA_SWAP(id) \ #define DATA_SWAP(id) \
{ \ { \
UserDef userdef_tmp; \ UserDef userdef_tmp; \
@ -387,12 +392,12 @@ void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *use
} \ } \
((void)0) ((void)0)
std::swap(userdef_a->uistyles, userdef_b->uistyles); VALUE_SWAP(uistyles);
std::swap(userdef_a->uifonts, userdef_b->uifonts); VALUE_SWAP(uifonts);
std::swap(userdef_a->themes, userdef_b->themes); VALUE_SWAP(themes);
std::swap(userdef_a->addons, userdef_b->addons); VALUE_SWAP(addons);
std::swap(userdef_a->user_keymaps, userdef_b->user_keymaps); VALUE_SWAP(user_keymaps);
std::swap(userdef_a->user_keyconfig_prefs, userdef_b->user_keyconfig_prefs); VALUE_SWAP(user_keyconfig_prefs);
DATA_SWAP(font_path_ui); DATA_SWAP(font_path_ui);
DATA_SWAP(font_path_ui_mono); 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); DATA_SWAP(ui_scale);
#undef SWAP_TYPELESS #undef VALUE_SWAP
#undef DATA_SWAP #undef DATA_SWAP
#undef LISTBASE_SWAP
#undef FLAG_SWAP #undef FLAG_SWAP
} }

View File

@ -10,7 +10,9 @@
#include "BKE_curves.hh" #include "BKE_curves.hh"
#include "BKE_deform.hh" #include "BKE_deform.hh"
#include "BKE_grease_pencil.hh" #include "BKE_grease_pencil.hh"
#include "BKE_lib_id.hh"
#include "BKE_material.h" #include "BKE_material.h"
#include "BKE_object.hh"
#include "BLI_color.hh" #include "BLI_color.hh"
#include "BLI_listbase.h" #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. */ /* Get the number of points, number of strokes and the offsets for each stroke. */
Vector<int> offsets; Vector<int> offsets;
Vector<int8_t> curve_types;
offsets.append(0); offsets.append(0);
int num_strokes = 0; int num_strokes = 0;
int num_points = 0; int num_points = 0;
bool has_bezier_stroke = false;
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf.strokes) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf.strokes) {
num_points += gps->totpoints; if (gps->editcurve != nullptr) {
offsets.append(num_points); 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++; num_strokes++;
offsets.append(num_points);
} }
/* Resize the CurvesGeometry. */ /* 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(); OffsetIndices<int> points_by_curve = curves.points_by_curve();
MutableAttributeAccessor attributes = curves.attributes_for_write(); MutableAttributeAccessor attributes = curves.attributes_for_write();
/* All strokes are poly curves. */ if (!has_bezier_stroke) {
curves.fill_curve_types(CURVE_TYPE_POLY); /* 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. */ /* Find used vertex groups in this drawing. */
ListBase stroke_vertex_group_names; ListBase stroke_vertex_group_names;
@ -118,6 +136,12 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
/* Point Attributes. */ /* Point Attributes. */
MutableSpan<float3> positions = curves.positions_for_write(); 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> radii = drawing.radii_for_write();
MutableSpan<float> opacities = drawing.opacities_for_write(); MutableSpan<float> opacities = drawing.opacities_for_write();
SpanAttributeWriter<float> delta_times = attributes.lookup_or_add_for_write_span<float>( 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; int stroke_i = 0;
LISTBASE_FOREACH_INDEX (bGPDstroke *, gps, &gpf.strokes, stroke_i) { 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; stroke_cyclic.span[stroke_i] = (gps->flag & GP_STROKE_CYCLIC) != 0;
/* TODO: This should be a `double` attribute. */ /* TODO: This should be a `double` attribute. */
stroke_init_times.span[stroke_i] = float(gps->inittime); 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_fill_colors.span[stroke_i] = ColorGeometry4f(gps->vert_color_fill);
stroke_materials.span[stroke_i] = gps->mat_nr; stroke_materials.span[stroke_i] = gps->mat_nr;
/* Write point attributes. */ IndexRange points = points_by_curve[stroke_i];
IndexRange stroke_points_range = points_by_curve[stroke_i]; if (points.is_empty()) {
if (stroke_points_range.is_empty()) {
continue; continue;
} }
Span<bGPDspoint> stroke_points{gps->points, gps->totpoints}; const Span<bGPDspoint> src_points{gps->points, gps->totpoints};
MutableSpan<float3> stroke_positions = positions.slice(stroke_points_range); /* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px`
MutableSpan<float> stroke_radii = radii.slice(stroke_points_range); * was the brush size which would be stored in the stroke thickness and then scaled by the
MutableSpan<float> stroke_opacities = opacities.slice(stroke_points_range); * point pressure factor. Finally, the render engine would divide this thickness value by
MutableSpan<float> stroke_deltatimes = delta_times.span.slice(stroke_points_range); * 2000 (we're going from a thickness to a radius, hence the factor of two) to convert back
MutableSpan<float> stroke_rotations = rotations.span.slice(stroke_points_range); * into blender units. Store the radius now directly in blender units. This makes it
MutableSpan<ColorGeometry4f> stroke_vertex_colors = vertex_colors.span.slice( * consistent with how hair curves handle the radius. */
stroke_points_range); const float stroke_thickness = float(gps->thickness) / 2000.0f;
MutableSpan<bool> stroke_selections = selection.span.slice(stroke_points_range); MutableSpan<float3> dst_positions = positions.slice(points);
MutableSpan<MDeformVert> stroke_dverts = use_dverts ? dverts.slice(stroke_points_range) : MutableSpan<float3> dst_handle_positions_left = has_bezier_stroke ?
MutableSpan<MDeformVert>(); 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. */ if (curve_types[stroke_i] == CURVE_TYPE_POLY) {
const bGPDspoint &first_pt = stroke_points.first(); threading::parallel_for(src_points.index_range(), 4096, [&](const IndexRange range) {
stroke_positions.first() = float3(first_pt.x, first_pt.y, first_pt.z); for (const int point_i : range) {
/* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px` was const bGPDspoint &pt = src_points[point_i];
* the brush size which would be stored in the stroke thickness and then scaled by the point dst_positions[point_i] = float3(pt.x, pt.y, pt.z);
* pressure factor. Finally, the render engine would divide this thickness value by 2000 (we're dst_radii[point_i] = stroke_thickness * pt.pressure;
* going from a thickness to a radius, hence the factor of two) to convert back into blender dst_opacities[point_i] = pt.strength;
* units. dst_rotations[point_i] = pt.uv_rot;
* Store the radius now directly in blender units. This makes it consistent with how hair dst_vertex_colors[point_i] = ColorGeometry4f(pt.vert_color);
* curves handle the radius. */ dst_selection[point_i] = (pt.flag & GP_SPOINT_SELECT) != 0;
stroke_radii.first() = gps->thickness * first_pt.pressure / 2000.0f; if (use_dverts && gps->dvert) {
stroke_opacities.first() = first_pt.strength; copy_dvert(gps->dvert[point_i], dst_dverts[point_i]);
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) { dst_deltatimes.first() = 0;
copy_dvert(gps->dvert[0], stroke_dverts.first()); 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. */ threading::parallel_for(src_curve_points.index_range(), 4096, [&](const IndexRange range) {
for (const int i : stroke_points.index_range().drop_back(1)) { for (const int point_i : range) {
const int point_i = i + 1; const bGPDcurve_point &cpt = src_curve_points[point_i];
const bGPDspoint &pt_prev = stroke_points[point_i - 1]; dst_positions[point_i] = float3(cpt.bezt.vec[1]);
const bGPDspoint &pt = stroke_points[point_i]; dst_handle_positions_left[point_i] = float3(cpt.bezt.vec[0]);
stroke_positions[point_i] = float3(pt.x, pt.y, pt.z); dst_handle_positions_right[point_i] = float3(cpt.bezt.vec[2]);
stroke_radii[point_i] = gps->thickness * pt.pressure / 2000.0f; dst_radii[point_i] = stroke_thickness * cpt.pressure;
stroke_opacities[point_i] = pt.strength; dst_opacities[point_i] = cpt.strength;
stroke_deltatimes[point_i] = pt.time - pt_prev.time; dst_rotations[point_i] = cpt.uv_rot;
stroke_rotations[point_i] = pt.uv_rot; dst_vertex_colors[point_i] = ColorGeometry4f(cpt.vert_color);
stroke_vertex_colors[point_i] = ColorGeometry4f(pt.vert_color); dst_selection[point_i] = (cpt.flag & GP_CURVE_POINT_SELECT) != 0;
stroke_selections[point_i] = (pt.flag & GP_SPOINT_SELECT) != 0; if (use_dverts && gps->dvert) {
if (use_dverts && gps->dvert) { copy_dvert(gps->dvert[point_i], dst_dverts[point_i]);
copy_dvert(gps->dvert[point_i], stroke_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); 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 } // namespace blender::bke::greasepencil::convert

View File

@ -107,6 +107,7 @@ static void id_type_init()
init_types_num++; init_types_num++;
BLI_assert_msg(init_types_num == INDEX_ID_MAX, "Some IDTypeInfo initialization is missing"); BLI_assert_msg(init_types_num == INDEX_ID_MAX, "Some IDTypeInfo initialization is missing");
UNUSED_VARS_NDEBUG(init_types_num);
#undef INIT_TYPE #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) { if (id->override_library == nullptr) {
return; 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) { if (id->override_library->reference == nullptr) {
/* This (probably) used to be a template ID, could be linked or local, not an override. */ /* This (probably) used to be a template ID, could be linked or local, not an override. */
BKE_reportf(reports, 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 " "Library override templates have been removed: removing all override data from "
"the data-block '%s'", "the data-block '%s'",
id->name); id->name);
BKE_lib_override_library_free(&id->override_library, true); BKE_lib_override_library_make_local(nullptr, id);
return; return;
} }
if (id->override_library->reference == id) { 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, " "Data corruption: data-block '%s' is using itself as library override reference, "
"removing all override data", "removing all override data",
id->name); id->name);
BKE_lib_override_library_free(&id->override_library, true); BKE_lib_override_library_make_local(nullptr, id);
return; return;
} }
if (!ID_IS_LINKED(id->override_library->reference)) { 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", "library override reference, removing all override data",
id->name, id->name,
id->override_library->reference->name); id->override_library->reference->name);
BKE_lib_override_library_free(&id->override_library, true); BKE_lib_override_library_make_local(nullptr, id);
return; return;
} }
} }

View File

@ -316,8 +316,8 @@ TEST(lib_remap, never_null_usage_storage_requested_on_delete)
remapper, remapper,
(ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_STORE_NEVER_NULL_USAGE)); (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 /* Never null usages un-assignment is not enforced (no #ID_REMAP_FORCE_NEVER_NULL_USAGE),
* obdta should still use the original mesh. */ * so the object-data should still use the original mesh. */
EXPECT_EQ(context.test_data.object->data, context.test_data.mesh); EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
EXPECT_NE(context.test_data.object->data, nullptr); EXPECT_NE(context.test_data.object->data, nullptr);
EXPECT_TRUE(remapper.never_null_users().contains(&context.test_data.object->id)); 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 /** \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. */ /* Array scan index. */
int i; int i;
int minmin, minmax; const int minmin = 0;
int maxmin, maxmax; const int maxmax = points_num - 1;
int minmax;
int maxmin;
float xmax; float xmax;
/* Get the indices of points with min X-coord and min|max Y-coord. */ /* Get the indices of points with min X-coord and min|max Y-coord. */
float xmin = points[0][0]; float xmin = points[0][0];
for (i = 1; i < points_num; i++) { for (i = 1; i <= maxmax; i++) {
if (points[i][0] != xmin) { if (points[i][0] != xmin) {
break; break;
} }
} }
minmin = 0;
minmax = i - 1; 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; r_points[++top] = minmin;
if (points[minmax][1] != points[minmin][1]) { if (points[minmax][1] != points[minmin][1]) {
/* A nontrivial segment. */ /* 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. */ /* Get the indices of points with max X-coord and min|max Y-coord. */
maxmax = points_num - 1; xmax = points[maxmax][0];
xmax = points[points_num - 1][0]; for (i = maxmax - 1; i >= 0; i--) {
for (i = points_num - 2; i >= 0; i--) {
if (points[i][0] != xmax) { if (points[i][0] != xmax) {
break; break;
} }
@ -235,12 +236,12 @@ static float convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[
int points_hull_num) int points_hull_num)
{ {
float area_best = FLT_MAX; 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++) { for (int i = 0, i_prev = points_hull_num - 1; i < points_hull_num; i_prev = i++) {
/* 2D rotation matrix. */ /* 2D rotation matrix. */
float dvec_length = 0.0f; 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); float2(points_hull[i]) - float2(points_hull[i_prev]), dvec_length);
if (UNLIKELY(dvec_length == 0.0f)) { if (UNLIKELY(dvec_length == 0.0f)) {
continue; 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++) { for (int j = 0; j < points_hull_num; j++) {
const float2 tvec = { const float2 tvec = {
mul_v2_v2_cw_x(dvec, points_hull[j]), sincos_rotate_cw_x(sincos, points_hull[j]),
mul_v2_v2_cw_y(dvec, points_hull[j]), sincos_rotate_cw_y(sincos, points_hull[j]),
}; };
bounds[0].min = math::min(bounds[0].min, tvec[0]); 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) { if (area_test < area_best) {
area_best = area_test; 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> template<int Axis, int AxisSign>
static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2], static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2],
const int points_hull_num, const int points_hull_num,
const float2 &dvec, const float2 &sincos,
int *index_p) int *index_p)
{ {
/* NOTE(@ideasman42): This could be optimized to use a search strategy /* 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; const int index_init = *index_p;
int index_best = index_init; int index_best = index_init;
float value_init = (Axis == 0) ? mul_v2_v2_cw_x(dvec, points_hull[index_best]) : float value_init = (Axis == 0) ? sincos_rotate_cw_x(sincos, points_hull[index_best]) :
mul_v2_v2_cw_y(dvec, points_hull[index_best]); sincos_rotate_cw_y(sincos, points_hull[index_best]);
float value_best = value_init; float value_best = value_init;
/* Simply scan up the array. */ /* Simply scan up the array. */
for (int count = 1; count < points_hull_num; count++) { for (int count = 1; count < points_hull_num; count++) {
const int index_test = (index_init + count) % points_hull_num; 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]) : const float value_test = (Axis == 0) ? sincos_rotate_cw_x(sincos, points_hull[index_test]) :
mul_v2_v2_cw_y(dvec, points_hull[index_test]); sincos_rotate_cw_y(sincos, points_hull[index_test]);
if ((AxisSign == -1) ? (value_test > value_best) : (value_test < value_best)) { if ((AxisSign == -1) ? (value_test > value_best) : (value_test < value_best)) {
break; 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) static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int points_hull_num)
{ {
float area_best = FLT_MAX; 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; bool is_first = true;
/* Initialize to zero because the first pass uses the first index to set the bounds. */ /* 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++) { for (int i = 0, i_prev = points_hull_num - 1; i < points_hull_num; i_prev = i++) {
/* 2D rotation matrix. */ /* 2D rotation matrix. */
float dvec_length = 0.0f; 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); float2(points_hull[i]) - float2(points_hull[i_prev]), dvec_length);
if (UNLIKELY(dvec_length == 0.0f)) { if (UNLIKELY(dvec_length == 0.0f)) {
continue; continue;
@ -348,16 +349,16 @@ static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int poin
blender::Bounds<float> bounds[2]; blender::Bounds<float> bounds[2];
bounds[0].min = bounds[0].max = mul_v2_v2_cw_x(dvec, points_hull[0]); bounds[0].min = bounds[0].max = sincos_rotate_cw_x(sincos, points_hull[0]);
bounds[1].min = bounds[1].max = mul_v2_v2_cw_y(dvec, 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[0].min = bounds_index[0].max = 0;
bounds_index[1].min = bounds_index[1].max = 0; bounds_index[1].min = bounds_index[1].max = 0;
for (int j = 1; j < points_hull_num; j++) { for (int j = 1; j < points_hull_num; j++) {
const float2 tvec = { const float2 tvec = {
mul_v2_v2_cw_x(dvec, points_hull[j]), sincos_rotate_cw_x(sincos, points_hull[j]),
mul_v2_v2_cw_y(dvec, points_hull[j]), sincos_rotate_cw_y(sincos, points_hull[j]),
}; };
for (int axis = 0; axis < 2; axis++) { for (int axis = 0; axis < 2; axis++) {
if (tvec[axis] < bounds[axis].min) { 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); area_best = (bounds[0].max - bounds[0].min) * (bounds[1].max - bounds[1].min);
dvec_best = dvec; sincos_best = sincos;
continue; 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] = { blender::Bounds<float> bounds_test[2] = {
{convexhull_2d_compute_extent_on_axis<0, -1>( {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>( 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>( {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>( 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) { if (area_test < area_best) {
area_best = area_test; 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 #ifdef USE_BRUTE_FORCE_ASSERT
/* Ensure the optimized result matches the brute-force version. */ /* Ensure the optimized result matches the brute-force version. */

View File

@ -59,7 +59,6 @@ set(SRC
set(LIB set(LIB
PRIVATE bf::animrig PRIVATE bf::animrig
PRIVATE bf::blenfont
bf_blenkernel bf_blenkernel
PRIVATE bf::blenlib PRIVATE bf::blenlib
PRIVATE bf::depsgraph PRIVATE bf::depsgraph
@ -118,6 +117,7 @@ if(WITH_GTESTS)
) )
set(TEST_UTIL_LIB set(TEST_UTIL_LIB
${LIB} ${LIB}
PRIVATE bf::blenfont
bf_blenloader bf_blenloader
) )
blender_add_lib(bf_blenloader_test_util "${TEST_UTIL_SRC}" "${TEST_UTIL_INC}" "${TEST_UTIL_INC_SYS}" "${TEST_UTIL_LIB}") 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, void Profiler::add_execution_time(const bNodeInstanceKey key,
const timeit::Nanoseconds &execution_time) const timeit::Nanoseconds &execution_time)
{ {
data_.per_node_execution_time.add_or_modify( data_.per_node_execution_time.lookup_or_add(key, timeit::Nanoseconds(0)) += execution_time;
key,
[&](timeit::Nanoseconds *total_execution_time) { *total_execution_time = execution_time; },
[&](timeit::Nanoseconds *total_execution_time) { *total_execution_time += execution_time; });
} }
void Profiler::finalize(const bNodeTree &node_tree) 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() 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()) { if (is_multi_layer()) {
execute_multi_layer(); execute_multi_layer();
} }

View File

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

View File

@ -117,6 +117,18 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
return; 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_x = this->get_delta_x();
const int delta_y = this->get_delta_y(); const int delta_y = this->get_delta_y();
for (int y = area.ymin; y < area.ymax; y++) { for (int y = area.ymin; y < area.ymax; y++) {

View File

@ -112,6 +112,10 @@ class Context {
* render pipeline. */ * render pipeline. */
virtual RenderContext *render_context() const; 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 /* 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 * 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. */ * 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_math_vector.hh"
#include "BLI_rect.h" #include "BLI_rect.h"
#include "DNA_node_types.h"
#include "DNA_vec_types.h" #include "DNA_vec_types.h"
#include "GPU_shader.h" #include "GPU_shader.h"
#include "BKE_node_runtime.hh"
#include "COM_context.hh" #include "COM_context.hh"
#include "COM_render_context.hh" #include "COM_render_context.hh"
#include "COM_static_cache_manager.hh" #include "COM_static_cache_manager.hh"
@ -23,6 +26,14 @@ RenderContext *Context::render_context() const
return nullptr; 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 int2 Context::get_compositing_region_size() const
{ {
const rcti compositing_region = get_compositing_region(); 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; 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; fcu = (FCurve *)ale->key_data;
success = (fcu != nullptr); success = (fcu != nullptr);

View File

@ -8,6 +8,7 @@
#include "BKE_context.hh" #include "BKE_context.hh"
#include "BKE_grease_pencil.hh" #include "BKE_grease_pencil.hh"
#include "BKE_report.hh"
#include "DEG_depsgraph.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) 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); Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data); GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
const int affect_visibility = RNA_boolean_get(op->ptr, "affect_visibility"); 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 */ /* properties */
RNA_def_boolean(ot->srna, "lock", true, "Lock Value", "Lock/Unlock all layers"); 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 } // namespace blender::ed::greasepencil
void ED_operatortypes_grease_pencil_layers() 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_reveal);
WM_operatortype_append(GREASE_PENCIL_OT_layer_isolate); WM_operatortype_append(GREASE_PENCIL_OT_layer_isolate);
WM_operatortype_append(GREASE_PENCIL_OT_layer_lock_all); 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); 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. */ /* Index of this drawing in the original combined array of all drawings in GreasePencil ID. */
int index_; 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_; uint32_t flag_;
protected: 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 * 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, void decode_valid_drawingtype_at_index_ensure(MutableSpan<GreasePencilDrawingBase *> &drawings,
const GreasePencilDrawingType drawing_type) const 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_path(uiBut *but, const char *path);
void UI_but_drag_set_name(uiBut *but, const char *name); 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. * 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; 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) 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 */ 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->icon,
but->dragtype, but->dragtype,
but->dragpoin, but->dragpoin,
ui_but_value_get(but),
(but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA : (but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA :
WM_DRAG_NOP); WM_DRAG_NOP);
/* wmDrag has ownership over dragpoin now, stop messing with it. */ /* 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 /* Special feature for assets: We add another drag item that supports multiple assets. It
* gets the assets from context. */ * gets the assets from context. */
if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { 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) */ /* coords are Window/uiBlock relative (depends on the button) */
int draglastx, draglasty; int draglastx, draglasty;
int dragstartx, dragstarty; int dragstartx, dragstarty;
int draglastvalue;
int dragstartvalue;
bool dragchange, draglock; bool dragchange, draglock;
int dragsel; int dragsel;
float dragf, dragfstart; float dragf, dragfstart;
@ -435,7 +433,6 @@ struct uiHandleButtonData {
/* Menu open, see: #UI_screen_free_active_but_highlight. */ /* Menu open, see: #UI_screen_free_active_but_highlight. */
uiPopupBlockHandle *menu; uiPopupBlockHandle *menu;
int menuretval;
/* Search box see: #UI_screen_free_active_but_highlight. */ /* Search box see: #UI_screen_free_active_but_highlight. */
ARegion *searchbox; ARegion *searchbox;
@ -2174,7 +2171,7 @@ static bool ui_but_drag_init(bContext *C,
} }
if (valid) { 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 { else {
MEM_freeN(drag_info); MEM_freeN(drag_info);

View File

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

View File

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

View File

@ -3222,8 +3222,6 @@ static int object_convert_exec(bContext *C, wmOperator *op)
{ {
ob->flag |= OB_DONE; ob->flag |= OB_DONE;
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
if (keep_original) { if (keep_original) {
BLI_assert_unreachable(); BLI_assert_unreachable();
} }
@ -3231,16 +3229,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
newob = ob; newob = ob;
} }
GreasePencil *new_grease_pencil = static_cast<GreasePencil *>( bke::greasepencil::convert::legacy_gpencil_object(*bmain, *newob);
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);
} }
else if (target == OB_CURVES) { else if (target == OB_CURVES) {
ob->flag |= OB_DONE; ob->flag |= OB_DONE;

View File

@ -150,12 +150,12 @@ static void console_cursor(wmWindow *win, ScrArea * /*area*/, ARegion *region)
/* ************* dropboxes ************* */ /* ************* 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; 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); 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()); 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); 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]; char pathname[FILE_MAX + 2];
SNPRINTF(pathname, "\"%s\"", WM_drag_get_single_path(drag)); SNPRINTF(pathname, "\"%s\"", WM_drag_get_single_path(drag));
RNA_string_set(drop->ptr, "text", pathname); 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 */ /* this region dropbox definition */
static void console_dropboxes() static void console_dropboxes()
{ {
ListBase *lb = WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW); 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(
WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, nullptr, nullptr); 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 *********** */ /* ************* end drop *********** */

View File

@ -1453,7 +1453,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, wmOperator * /*op*/, cons
TSE_GPENCIL_EFFECT_BASE); TSE_GPENCIL_EFFECT_BASE);
const eWM_DragDataType wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID; 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) { if (use_datastack_drag) {
TreeElement *te_bone = nullptr; TreeElement *te_bone = nullptr;

View File

@ -301,7 +301,7 @@ static void text_cursor(wmWindow *win, ScrArea *area, ARegion *region)
/* ************* dropboxes ************* */ /* ************* 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) { if (drag->type == WM_DRAG_PATH) {
const eFileSel_File_Types file_type = eFileSel_File_Types(WM_drag_get_path_file_type(drag)); 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; 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 */ /* copy drag path to properties */
RNA_string_set(drop->ptr, "filepath", WM_drag_get_single_path(drag)); 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); 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); 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()); 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 */ /* this region dropbox definition */
static void text_dropboxes() static void text_dropboxes()
{ {
ListBase *lb = WM_dropboxmap_find("Text", SPACE_TEXT, RGN_TYPE_WINDOW); 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_open", text_drop_path_poll, text_drop_path_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_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 *********** */ /* ************* end drop *********** */

View File

@ -335,7 +335,12 @@ static void TimeToTransData(
copy_v3_v3(td->iloc, td->loc); copy_v3_v3(td->iloc, td->loc);
td->val = time; td->val = time;
td->ival = *(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; td->center[1] = ypos;
/* Store the AnimData where this keyframe exists as a keyframe of the /* 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); float overshoot_point_param = used_percent_length * (new_size - 1);
if (start_points[curve]) { if (start_points[curve]) {
/** Here we use the vector between two adjacent points around #overshoot_point_param as /** 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. * using the vector (a_few_points_back - end_point) might be a better solution in the future.
*/ */
int index1 = math::floor(overshoot_point_param); 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); OffsetIndices dst_indices = offset_indices::accumulate_counts_to_offsets(dst_points_by_curve);
int target_point_count = dst_points_by_curve.last(); 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); Array<int> dst_to_src_point(target_point_count);
for (const int curve : src_curves.curves_range()) { for (const int curve : src_curves.curves_range()) {
const int point_count = points_by_curve[curve].size(); 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); 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. * This is used for platform support, where bug reports can list all failing shaders.
*/ */
void GPU_shader_compile_static(); void GPU_shader_compile_static();
/** DEPRECATED: Use hardcoded buffer location instead. */ /** DEPRECATED: Use hard-coded buffer location instead. */
typedef enum { typedef enum {
GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */
GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */
@ -353,7 +353,7 @@ typedef enum {
GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
} GPUUniformBlockBuiltin; } GPUUniformBlockBuiltin;
/** DEPRECATED: Use hardcoded buffer location instead. */ /** DEPRECATED: Use hard-coded buffer location instead. */
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin); int GPU_shader_get_builtin_block(GPUShader *shader, int builtin);
/** DEPRECATED: Kept only because of Python GPU API. */ /** 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); Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::INT); BLI_assert(shader.constants.types[location] == gpu::shader::Type::INT);
shader.constants.values[location].i = value; shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].i, value);
shader.constants.is_dirty = true;
} }
void GPU_shader_constant_uint_ex(GPUShader *sh, int location, uint value) void GPU_shader_constant_uint_ex(GPUShader *sh, int location, uint value)
{ {
Shader &shader = *unwrap(sh); Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::UINT); BLI_assert(shader.constants.types[location] == gpu::shader::Type::UINT);
shader.constants.values[location].u = value; shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].u, value);
shader.constants.is_dirty = true;
} }
void GPU_shader_constant_float_ex(GPUShader *sh, int location, float value) void GPU_shader_constant_float_ex(GPUShader *sh, int location, float value)
{ {
Shader &shader = *unwrap(sh); Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::FLOAT); BLI_assert(shader.constants.types[location] == gpu::shader::Type::FLOAT);
shader.constants.values[location].f = value; shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].f, value);
shader.constants.is_dirty = true;
} }
void GPU_shader_constant_bool_ex(GPUShader *sh, int location, bool value) void GPU_shader_constant_bool_ex(GPUShader *sh, int location, bool value)
{ {
Shader &shader = *unwrap(sh); Shader &shader = *unwrap(sh);
BLI_assert(shader.constants.types[location] == gpu::shader::Type::BOOL); BLI_assert(shader.constants.types[location] == gpu::shader::Type::BOOL);
shader.constants.values[location].u = value; shader.constants.is_dirty |= assign_if_different(shader.constants.values[location].u,
shader.constants.is_dirty = true; static_cast<uint32_t>(value));
} }
void GPU_shader_constant_int(GPUShader *sh, const char *name, int 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/call_method.hpp>
#include <boost/python/class.hpp> #include <boost/python/class.hpp>
#include <boost/python/import.hpp> #include <boost/python/import.hpp>
#include <boost/python/object.hpp>
#include <boost/python/return_value_policy.hpp> #include <boost/python/return_value_policy.hpp>
#include <boost/python/to_python_converter.hpp> #include <boost/python/to_python_converter.hpp>
#include "BLI_listbase.h" #include "BLI_utildefines.h"
#include "BKE_report.hh" #include "BKE_report.hh"
@ -22,49 +21,52 @@
#include "RNA_types.hh" #include "RNA_types.hh"
#include "bpy_rna.h" #include "bpy_rna.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include <list> #include <list>
#include <memory>
using namespace boost; using namespace boost;
namespace blender::io::usd { namespace blender::io::usd {
using USDHookList = std::list<USDHook *>; using USDHookList = std::list<std::unique_ptr<USDHook>>;
/* USD hook type declarations */ /* USD hook type declarations */
static USDHookList g_usd_hooks; static USDHookList &hook_list()
void USD_register_hook(USDHook *hook)
{ {
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. */ /* The hook is already in the list. */
return; return;
} }
/* Add hook type to the list. */ /* 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) 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 */ /* sanity checks */
if (g_usd_hooks.empty() || (name == nullptr) || (name[0] == 0)) { if (hook_list().empty() || (idname == nullptr) || (idname[0] == 0)) {
return nullptr; return nullptr;
} }
USDHookList::iterator hook_iter = std::find_if( USDHookList::iterator hook_iter = std::find_if(
g_usd_hooks.begin(), g_usd_hooks.end(), [name](USDHook *hook) { hook_list().begin(), hook_list().end(), [idname](const std::unique_ptr<USDHook> &item) {
return STREQ(hook->idname, name); 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*. */ /* Convert PointerRNA to a PyObject*. */
@ -136,7 +138,7 @@ void register_hook_converters()
static bool registered = false; static bool registered = false;
/* No need to register if there are no hooks. */ /* No need to register if there are no hooks. */
if (g_usd_hooks.empty()) { if (hook_list().empty()) {
return; return;
} }
@ -196,22 +198,22 @@ class USDHookInvoker {
/* Attempt to call the function, if defined by the registered hooks. */ /* Attempt to call the function, if defined by the registered hooks. */
void call() const void call() const
{ {
if (g_usd_hooks.empty()) { if (hook_list().empty()) {
return; return;
} }
PyGILState_STATE gilstate = PyGILState_Ensure(); PyGILState_STATE gilstate = PyGILState_Ensure();
/* Iterate over the hooks and invoke the hook function, if it's defined. */ /* Iterate over the hooks and invoke the hook function, if it's defined. */
USDHookList::const_iterator hook_iter = g_usd_hooks.begin(); USDHookList::const_iterator hook_iter = hook_list().begin();
while (hook_iter != g_usd_hooks.end()) { while (hook_iter != hook_list().end()) {
/* XXX: Not sure if this is necessary: /* XXX: Not sure if this is necessary:
* Advance the iterator before invoking the callback, to guard * Advance the iterator before invoking the callback, to guard
* against the unlikely error where the hook is de-registered in * against the unlikely error where the hook is de-registered in
* the callback. This would prevent a crash due to the iterator * the callback. This would prevent a crash due to the iterator
* getting invalidated. */ * getting invalidated. */
USDHook *hook = *hook_iter; USDHook *hook = hook_iter->get();
++hook_iter; ++hook_iter;
if (!hook->rna_ext.data) { if (!hook->rna_ext.data) {
@ -329,7 +331,7 @@ class OnImportInvoker : public USDHookInvoker {
void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports) void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports)
{ {
if (g_usd_hooks.empty()) { if (hook_list().empty()) {
return; return;
} }
@ -342,7 +344,7 @@ void call_material_export_hooks(pxr::UsdStageRefPtr stage,
pxr::UsdShadeMaterial &usd_material, pxr::UsdShadeMaterial &usd_material,
ReportList *reports) ReportList *reports)
{ {
if (g_usd_hooks.empty()) { if (hook_list().empty()) {
return; return;
} }
@ -352,7 +354,7 @@ void call_material_export_hooks(pxr::UsdStageRefPtr stage,
void call_import_hooks(pxr::UsdStageRefPtr stage, ReportList *reports) void call_import_hooks(pxr::UsdStageRefPtr stage, ReportList *reports)
{ {
if (g_usd_hooks.empty()) { if (hook_list().empty()) {
return; return;
} }

View File

@ -204,13 +204,12 @@ struct USDHook {
ExtensionRNA rna_ext; 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. * Remove the given entry from the list of registered hooks and
* Note that this does not free the allocated memory for the * free the allocated memory for the hook instance.
* hook instance, so a separate call to `MEM_freeN(hook)` is required.
*/ */
void USD_unregister_hook(USDHook *hook); 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 }; // namespace blender::io::usd

View File

@ -923,4 +923,24 @@
.step = 4,\ .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 */ /* clang-format off */

View File

@ -108,6 +108,8 @@ typedef enum ModifierType {
eModifierType_GreasePencilDash = 71, eModifierType_GreasePencilDash = 71,
eModifierType_GreasePencilMultiply = 72, eModifierType_GreasePencilMultiply = 72,
eModifierType_GreasePencilLength = 73, eModifierType_GreasePencilLength = 73,
eModifierType_GreasePencilWeightAngle = 74,
eModifierType_GreasePencilArray = 75,
NUM_MODIFIER_TYPES, NUM_MODIFIER_TYPES,
} ModifierType; } ModifierType;
@ -2867,3 +2869,60 @@ typedef struct GreasePencilLengthModifierData {
void *_pad1; void *_pad1;
} GreasePencilLengthModifierData; } 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(GreasePencilDashModifierSegment);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilMultiModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilMultiModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilWeightAngleModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilArrayModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT #undef SDNA_DEFAULT_DECL_STRUCT
@ -557,6 +559,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilSubdivModifierData), SDNA_DEFAULT_DECL(GreasePencilSubdivModifierData),
SDNA_DEFAULT_DECL(GreasePencilNoiseModifierData), SDNA_DEFAULT_DECL(GreasePencilNoiseModifierData),
SDNA_DEFAULT_DECL(GreasePencilLengthModifierData), SDNA_DEFAULT_DECL(GreasePencilLengthModifierData),
SDNA_DEFAULT_DECL(GreasePencilWeightAngleModifierData),
/* Grease Pencil 3.0 defaults. */ /* Grease Pencil 3.0 defaults. */
SDNA_DEFAULT_DECL(GreasePencilSmoothModifierData), SDNA_DEFAULT_DECL(GreasePencilSmoothModifierData),
@ -600,6 +603,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilDashModifierSegment), SDNA_DEFAULT_DECL(GreasePencilDashModifierSegment),
SDNA_DEFAULT_DECL(GreasePencilDashModifierData), SDNA_DEFAULT_DECL(GreasePencilDashModifierData),
SDNA_DEFAULT_DECL(GreasePencilMultiModifierData), SDNA_DEFAULT_DECL(GreasePencilMultiModifierData),
SDNA_DEFAULT_DECL(GreasePencilArrayModifierData),
}; };
#undef SDNA_DEFAULT_DECL #undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX #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)"); prop, "Has Motion Paths", "Are there any bone paths that will need updating (read-only)");
/* If enabled, bakes the motion paths into camera space. */ /* 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_boolean_sdna(prop, nullptr, "path_bakeflag", MOTIONPATH_BAKE_CAMERA_SPACE);
RNA_def_property_ui_text( RNA_def_property_ui_text(
prop, prop,

View File

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

View File

@ -116,6 +116,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_OPACITY, ICON_MOD_OPACITY,
"Opacity", "Opacity",
"Change the opacity of the strokes"}, "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), RNA_ENUM_ITEM_HEADING(N_("Generate"), nullptr),
{eModifierType_Array, {eModifierType_Array,
@ -211,6 +216,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_WIREFRAME, ICON_MOD_WIREFRAME,
"Wireframe", "Wireframe",
"Convert faces into thickened edges"}, "Convert faces into thickened edges"},
{eModifierType_GreasePencilArray,
"GREASE_PENCIL_ARRAY",
ICON_MOD_ARRAY,
"Array strokes",
"Duplicate strokes into an array"},
{eModifierType_GreasePencilLength, {eModifierType_GreasePencilLength,
"GREASE_PENCIL_LENGTH", "GREASE_PENCIL_LENGTH",
ICON_MOD_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(WeightedNormal, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(Weld, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Weld, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(Wireframe, 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) 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(GreasePencilDash);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilMulti); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilMulti);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilLength); 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(GreasePencilOffset);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity); 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(GreasePencilThick);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLattice); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLattice);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLength); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilLength);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightAngle);
static void rna_GreasePencilOpacityModifier_opacity_factor_range( static void rna_GreasePencilOpacityModifier_opacity_factor_range(
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) 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); 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) static void rna_def_modifier_grease_pencil_lattice(BlenderRNA *brna)
{ {
StructRNA *srna; StructRNA *srna;
@ -8794,6 +8918,85 @@ static void rna_def_modifier_grease_pencil_dash(BlenderRNA *brna)
RNA_define_lib_overridable(false); 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) static void rna_def_modifier_grease_pencil_multiply(BlenderRNA *brna)
{ {
StructRNA *srna; StructRNA *srna;
@ -9035,6 +9238,8 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_dash(brna); rna_def_modifier_grease_pencil_dash(brna);
rna_def_modifier_grease_pencil_multiply(brna); rna_def_modifier_grease_pencil_multiply(brna);
rna_def_modifier_grease_pencil_length(brna); rna_def_modifier_grease_pencil_length(brna);
rna_def_modifier_grease_pencil_weight_angle(brna);
rna_def_modifier_grease_pencil_array(brna);
} }
#endif #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_ui_text(prop, "Show Handles", "Show handles of Bézier control points");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, nullptr); 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_boolean_sdna(prop, nullptr, "flag", SIPO_AUTOLOCK_AXIS);
RNA_def_property_ui_text(prop, RNA_def_property_ui_text(prop,
"Auto-Lock Key Axis", "Auto-Lock Key Axis",

View File

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

View File

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

View File

@ -86,6 +86,8 @@ extern ModifierTypeInfo modifierType_GreasePencilLattice;
extern ModifierTypeInfo modifierType_GreasePencilDash; extern ModifierTypeInfo modifierType_GreasePencilDash;
extern ModifierTypeInfo modifierType_GreasePencilMultiply; extern ModifierTypeInfo modifierType_GreasePencilMultiply;
extern ModifierTypeInfo modifierType_GreasePencilLength; extern ModifierTypeInfo modifierType_GreasePencilLength;
extern ModifierTypeInfo modifierType_GreasePencilWeightAngle;
extern ModifierTypeInfo modifierType_GreasePencilArray;
/* MOD_util.cc */ /* 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 /* Always do the stretching first since it might depend on points which could be deleted by the
* shrink. */ * shrink. */
if (mmd.start_fac < 0.0f || mmd.end_fac < 0.0f || needs_additional_shrinking) { 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 /* #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 substract it from the back when 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 * 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. */ * mode since the function itself will need length to be sampled anyway. */
Array<float> starts(curves.curves_num()); Array<float> starts(curves.curves_num());
Array<float> ends(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(GreasePencilDash);
INIT_TYPE(GreasePencilMultiply); INIT_TYPE(GreasePencilMultiply);
INIT_TYPE(GreasePencilLength); INIT_TYPE(GreasePencilLength);
INIT_TYPE(GreasePencilWeightAngle);
INIT_TYPE(GreasePencilArray);
#undef INIT_TYPE #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; 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 { class DenoiseOperation : public NodeOperation {
public: public:
using NodeOperation::NodeOperation; using NodeOperation::NodeOperation;
@ -108,6 +119,7 @@ class DenoiseOperation : public NodeOperation {
filter.setImage("output", color, oidn::Format::Float3, width, height, 0, pixel_stride); filter.setImage("output", color, oidn::Format::Float3, width, height, 0, pixel_stride);
filter.set("hdr", use_hdr()); filter.set("hdr", use_hdr());
filter.set("cleanAux", auxiliary_passes_are_clean()); 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 /* 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. */ * 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); "albedo", albedo, oidn::Format::Float3, width, height, 0, pixel_stride);
albedoFilter.setImage( albedoFilter.setImage(
"output", albedo, oidn::Format::Float3, width, height, 0, pixel_stride); "output", albedo, oidn::Format::Float3, width, height, 0, pixel_stride);
albedoFilter.setProgressMonitorFunction(oidn_progress_monitor_function, &context());
albedoFilter.commit(); albedoFilter.commit();
albedoFilter.execute(); albedoFilter.execute();
} }
@ -144,6 +157,7 @@ class DenoiseOperation : public NodeOperation {
"normal", normal, oidn::Format::Float3, width, height, 0, pixel_stride); "normal", normal, oidn::Format::Float3, width, height, 0, pixel_stride);
normalFilter.setImage( normalFilter.setImage(
"output", normal, oidn::Format::Float3, width, height, 0, pixel_stride); "output", normal, oidn::Format::Float3, width, height, 0, pixel_stride);
normalFilter.setProgressMonitorFunction(oidn_progress_monitor_function, &context());
normalFilter.commit(); normalFilter.commit();
normalFilter.execute(); 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. * Note that \a poin should be valid allocated and not on stack.
*/ */
void WM_event_start_drag( 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 * 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 * #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. * Note that \a poin should be valid allocated and not on stack.
*/ */
wmDrag *WM_drag_data_create( 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. * 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); 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 */ /* Set OpenGL viewport and scissor */
void wmViewport(const rcti *winrct); void wmViewport(const rcti *winrct);
void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct); void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct);

View File

@ -1143,7 +1143,14 @@ enum eWM_DragDataType {
WM_DRAG_RNA, WM_DRAG_RNA,
WM_DRAG_PATH, WM_DRAG_PATH,
WM_DRAG_NAME, 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_COLOR,
WM_DRAG_DATASTACK, WM_DRAG_DATASTACK,
WM_DRAG_ASSET_CATALOG, WM_DRAG_ASSET_CATALOG,
@ -1255,7 +1262,6 @@ struct wmDrag {
int icon; int icon;
eWM_DragDataType type; eWM_DragDataType type;
void *poin; void *poin;
double value;
/** If no icon but imbuf should be drawn around cursor. */ /** If no icon but imbuf should be drawn around cursor. */
const ImBuf *imb; const ImBuf *imb;

View File

@ -258,8 +258,7 @@ static void wm_dropbox_invoke(bContext *C, wmDrag *drag)
} }
} }
wmDrag *WM_drag_data_create( wmDrag *WM_drag_data_create(bContext *C, int icon, eWM_DragDataType type, void *poin, uint flags)
bContext *C, int icon, eWM_DragDataType type, void *poin, double value, uint flags)
{ {
wmDrag *drag = MEM_new<wmDrag>(__func__); wmDrag *drag = MEM_new<wmDrag>(__func__);
@ -302,7 +301,6 @@ wmDrag *WM_drag_data_create(
drag->poin = poin; drag->poin = poin;
break; break;
} }
drag->value = value;
return drag; return drag;
} }
@ -315,10 +313,9 @@ void WM_event_start_prepared_drag(bContext *C, wmDrag *drag)
wm_dropbox_invoke(C, drag); wm_dropbox_invoke(C, drag);
} }
void WM_event_start_drag( void WM_event_start_drag(bContext *C, int icon, eWM_DragDataType type, void *poin, uint flags)
bContext *C, int icon, eWM_DragDataType type, void *poin, double value, 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); 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); wm_drag_free_path_data(&path_data);
break; break;
} }
case WM_DRAG_STRING: {
std::string *str = static_cast<std::string *>(poin);
MEM_delete(str);
break;
}
default: default:
MEM_freeN(poin); MEM_freeN(poin);
break; break;
@ -920,6 +922,24 @@ int WM_drag_get_path_file_type(const wmDrag *drag)
return path_data->file_types[0]; 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 ***************** */ /* ************** draw ***************** */
static void wm_drop_operator_draw(const blender::StringRef name, int x, int y) 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]); int icon = ED_file_extension_icon((char *)stra->strings[0]);
wmDragPath *path_data = WM_drag_create_path_data( wmDragPath *path_data = WM_drag_create_path_data(
blender::Span((char **)stra->strings, stra->count)); 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. */ /* 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; 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[] = static const char arg_handle_debug_gpu_compile_shaders_set_doc[] =
"\n" "\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*/, static int arg_handle_debug_gpu_compile_shaders_set(int /*argc*/,
const char ** /*argv*/, const char ** /*argv*/,
void * /*data*/) void * /*data*/)