Mesh: Replace auto smooth with node group #108014
|
@ -233,14 +233,14 @@ ccl_device_inline float intersection_curve_shadow_transparency(
|
||||||
return (1.0f - u) * f0 + u * f1;
|
return (1.0f - u) * f0 + u * f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device_inline bool intersection_skip_self(ccl_private const RaySelfPrimitives &self,
|
ccl_device_inline bool intersection_skip_self(ccl_ray_data const RaySelfPrimitives &self,
|
||||||
const int object,
|
const int object,
|
||||||
const int prim)
|
const int prim)
|
||||||
{
|
{
|
||||||
return (self.prim == prim) && (self.object == object);
|
return (self.prim == prim) && (self.object == object);
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device_inline bool intersection_skip_self_shadow(ccl_private const RaySelfPrimitives &self,
|
ccl_device_inline bool intersection_skip_self_shadow(ccl_ray_data const RaySelfPrimitives &self,
|
||||||
const int object,
|
const int object,
|
||||||
const int prim)
|
const int prim)
|
||||||
{
|
{
|
||||||
|
@ -248,7 +248,7 @@ ccl_device_inline bool intersection_skip_self_shadow(ccl_private const RaySelfPr
|
||||||
((self.light_prim == prim) && (self.light_object == object));
|
((self.light_prim == prim) && (self.light_object == object));
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device_inline bool intersection_skip_self_local(ccl_private const RaySelfPrimitives &self,
|
ccl_device_inline bool intersection_skip_self_local(ccl_ray_data const RaySelfPrimitives &self,
|
||||||
const int prim)
|
const int prim)
|
||||||
{
|
{
|
||||||
return (self.prim == prim);
|
return (self.prim == prim);
|
||||||
|
|
|
@ -152,7 +152,7 @@ ccl_device_inline float3 sphg_dir(float theta, float gamma, float b)
|
||||||
fast_sincosf(theta, &sin_theta, &cos_theta);
|
fast_sincosf(theta, &sin_theta, &cos_theta);
|
||||||
fast_sincosf(gamma, &sin_gamma, &cos_gamma);
|
fast_sincosf(gamma, &sin_gamma, &cos_gamma);
|
||||||
|
|
||||||
if (b == 1.0f) {
|
if (b == 1.0f || fabsf(cos_gamma) < 1e-6f) {
|
||||||
sin_phi = sin_gamma;
|
sin_phi = sin_gamma;
|
||||||
cos_phi = cos_gamma;
|
cos_phi = cos_gamma;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ typedef unsigned long long uint64_t;
|
||||||
#define ccl_constant const
|
#define ccl_constant const
|
||||||
#define ccl_gpu_shared __shared__
|
#define ccl_gpu_shared __shared__
|
||||||
#define ccl_private
|
#define ccl_private
|
||||||
|
#define ccl_ray_data ccl_private
|
||||||
#define ccl_may_alias
|
#define ccl_may_alias
|
||||||
#define ccl_restrict __restrict__
|
#define ccl_restrict __restrict__
|
||||||
#define ccl_loop_no_unroll
|
#define ccl_loop_no_unroll
|
||||||
|
|
|
@ -41,6 +41,7 @@ typedef unsigned long long uint64_t;
|
||||||
#define ccl_constant const
|
#define ccl_constant const
|
||||||
#define ccl_gpu_shared __shared__
|
#define ccl_gpu_shared __shared__
|
||||||
#define ccl_private
|
#define ccl_private
|
||||||
|
#define ccl_ray_data ccl_private
|
||||||
#define ccl_may_alias
|
#define ccl_may_alias
|
||||||
#define ccl_restrict __restrict__
|
#define ccl_restrict __restrict__
|
||||||
#define ccl_loop_no_unroll
|
#define ccl_loop_no_unroll
|
||||||
|
|
|
@ -48,6 +48,11 @@ using namespace metal::raytracing;
|
||||||
#define ccl_constant constant
|
#define ccl_constant constant
|
||||||
#define ccl_gpu_shared threadgroup
|
#define ccl_gpu_shared threadgroup
|
||||||
#define ccl_private thread
|
#define ccl_private thread
|
||||||
|
#ifdef __METALRT__
|
||||||
|
# define ccl_ray_data ray_data
|
||||||
|
#else
|
||||||
|
# define ccl_ray_data ccl_private
|
||||||
|
#endif
|
||||||
#define ccl_may_alias
|
#define ccl_may_alias
|
||||||
#define ccl_restrict __restrict
|
#define ccl_restrict __restrict
|
||||||
#define ccl_loop_no_unroll
|
#define ccl_loop_no_unroll
|
||||||
|
|
|
@ -34,29 +34,6 @@ struct TriangleIntersectionResult {
|
||||||
|
|
||||||
enum { METALRT_HIT_TRIANGLE, METALRT_HIT_BOUNDING_BOX };
|
enum { METALRT_HIT_TRIANGLE, METALRT_HIT_BOUNDING_BOX };
|
||||||
|
|
||||||
/* Utilities. */
|
|
||||||
|
|
||||||
ccl_device_inline bool intersection_skip_self(ray_data const RaySelfPrimitives &self,
|
|
||||||
const int object,
|
|
||||||
const int prim)
|
|
||||||
{
|
|
||||||
return (self.prim == prim) && (self.object == object);
|
|
||||||
}
|
|
||||||
|
|
||||||
ccl_device_inline bool intersection_skip_self_shadow(ray_data const RaySelfPrimitives &self,
|
|
||||||
const int object,
|
|
||||||
const int prim)
|
|
||||||
{
|
|
||||||
return ((self.prim == prim) && (self.object == object)) ||
|
|
||||||
((self.light_prim == prim) && (self.light_object == object));
|
|
||||||
}
|
|
||||||
|
|
||||||
ccl_device_inline bool intersection_skip_self_local(ray_data const RaySelfPrimitives &self,
|
|
||||||
const int prim)
|
|
||||||
{
|
|
||||||
return (self.prim == prim);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hit functions. */
|
/* Hit functions. */
|
||||||
|
|
||||||
template<typename TReturn, uint intersection_type>
|
template<typename TReturn, uint intersection_type>
|
||||||
|
@ -72,7 +49,10 @@ TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal,
|
||||||
# ifdef __BVH_LOCAL__
|
# ifdef __BVH_LOCAL__
|
||||||
uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object);
|
uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object);
|
||||||
|
|
||||||
if ((object != payload.local_object) || intersection_skip_self_local(payload.self, prim)) {
|
MetalKernelContext context(launch_params_metal);
|
||||||
|
|
||||||
|
if ((object != payload.local_object) || context.intersection_skip_self_local(payload.self, prim))
|
||||||
|
{
|
||||||
/* Only intersect with matching object and skip self-intersecton. */
|
/* Only intersect with matching object and skip self-intersecton. */
|
||||||
result.accept = false;
|
result.accept = false;
|
||||||
result.continue_search = true;
|
result.continue_search = true;
|
||||||
|
@ -231,7 +211,9 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
if (intersection_skip_self_shadow(payload.self, object, prim)) {
|
MetalKernelContext context(launch_params_metal);
|
||||||
|
|
||||||
|
if (context.intersection_skip_self_shadow(payload.self, object, prim)) {
|
||||||
/* continue search */
|
/* continue search */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -246,8 +228,6 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
|
||||||
short num_hits = payload.num_hits;
|
short num_hits = payload.num_hits;
|
||||||
short num_recorded_hits = payload.num_recorded_hits;
|
short num_recorded_hits = payload.num_recorded_hits;
|
||||||
|
|
||||||
MetalKernelContext context(launch_params_metal);
|
|
||||||
|
|
||||||
/* If no transparent shadows, all light is blocked and we can stop immediately. */
|
/* If no transparent shadows, all light is blocked and we can stop immediately. */
|
||||||
if (num_hits >= max_hits ||
|
if (num_hits >= max_hits ||
|
||||||
!(context.intersection_get_shader_flags(NULL, prim, type) & SD_HAS_TRANSPARENT_SHADOW))
|
!(context.intersection_get_shader_flags(NULL, prim, type) & SD_HAS_TRANSPARENT_SHADOW))
|
||||||
|
@ -388,9 +368,11 @@ inline TReturnType metalrt_visibility_test(
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
MetalKernelContext context(launch_params_metal);
|
||||||
|
|
||||||
/* Shadow ray early termination. */
|
/* Shadow ray early termination. */
|
||||||
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
|
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
|
||||||
if (intersection_skip_self_shadow(payload.self, object, prim)) {
|
if (context.intersection_skip_self_shadow(payload.self, object, prim)) {
|
||||||
result.accept = false;
|
result.accept = false;
|
||||||
result.continue_search = true;
|
result.continue_search = true;
|
||||||
return result;
|
return result;
|
||||||
|
@ -402,7 +384,7 @@ inline TReturnType metalrt_visibility_test(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (intersection_skip_self(payload.self, object, prim)) {
|
if (context.intersection_skip_self(payload.self, object, prim)) {
|
||||||
result.accept = false;
|
result.accept = false;
|
||||||
result.continue_search = true;
|
result.continue_search = true;
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#define ccl_loop_no_unroll
|
#define ccl_loop_no_unroll
|
||||||
#define ccl_optional_struct_init
|
#define ccl_optional_struct_init
|
||||||
#define ccl_private
|
#define ccl_private
|
||||||
|
#define ccl_ray_data ccl_private
|
||||||
#define ccl_gpu_shared
|
#define ccl_gpu_shared
|
||||||
#define ATTR_FALLTHROUGH __attribute__((fallthrough))
|
#define ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||||
#define ccl_constant const
|
#define ccl_constant const
|
||||||
|
|
|
@ -48,6 +48,7 @@ typedef unsigned long long uint64_t;
|
||||||
#define ccl_constant const
|
#define ccl_constant const
|
||||||
#define ccl_gpu_shared __shared__
|
#define ccl_gpu_shared __shared__
|
||||||
#define ccl_private
|
#define ccl_private
|
||||||
|
#define ccl_ray_data ccl_private
|
||||||
#define ccl_may_alias
|
#define ccl_may_alias
|
||||||
#define ccl_restrict __restrict__
|
#define ccl_restrict __restrict__
|
||||||
#define ccl_loop_no_unroll
|
#define ccl_loop_no_unroll
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
# define ccl_inline_constant inline constexpr
|
# define ccl_inline_constant inline constexpr
|
||||||
# define ccl_constant const
|
# define ccl_constant const
|
||||||
# define ccl_private
|
# define ccl_private
|
||||||
|
# define ccl_ray_data ccl_private
|
||||||
|
|
||||||
# define ccl_restrict __restrict
|
# define ccl_restrict __restrict
|
||||||
# define ccl_optional_struct_init
|
# define ccl_optional_struct_init
|
||||||
|
|
|
@ -741,8 +741,34 @@ static string path_source_replace_includes_recursive(const string &source,
|
||||||
const string &source_filepath,
|
const string &source_filepath,
|
||||||
SourceReplaceState *state);
|
SourceReplaceState *state);
|
||||||
|
|
||||||
|
static string line_directive(const SourceReplaceState &state,
|
||||||
|
const string &path,
|
||||||
|
const size_t line_number)
|
||||||
|
{
|
||||||
|
string unescaped_path = path;
|
||||||
|
/* First we make path relative. */
|
||||||
|
if (string_startswith(unescaped_path, state.base.c_str())) {
|
||||||
|
const string base_file = path_filename(state.base);
|
||||||
|
const size_t base_len = state.base.length();
|
||||||
|
unescaped_path = base_file +
|
||||||
|
unescaped_path.substr(base_len, unescaped_path.length() - base_len);
|
||||||
|
}
|
||||||
|
/* Second, we replace all unsafe characters. */
|
||||||
|
const size_t length = unescaped_path.length();
|
||||||
|
string escaped_path = "";
|
||||||
|
for (size_t i = 0; i < length; ++i) {
|
||||||
|
const char ch = unescaped_path[i];
|
||||||
|
if (strchr("\"\'\?\\", ch) != nullptr) {
|
||||||
|
escaped_path += "\\";
|
||||||
|
}
|
||||||
|
escaped_path += ch;
|
||||||
|
}
|
||||||
|
return "#line " + std::to_string(line_number) + '"' + escaped_path + '"';
|
||||||
|
}
|
||||||
|
|
||||||
static string path_source_handle_preprocessor(const string &preprocessor_line,
|
static string path_source_handle_preprocessor(const string &preprocessor_line,
|
||||||
const string &source_filepath,
|
const string &source_filepath,
|
||||||
|
const size_t line_number,
|
||||||
SourceReplaceState *state)
|
SourceReplaceState *state)
|
||||||
{
|
{
|
||||||
string result = preprocessor_line;
|
string result = preprocessor_line;
|
||||||
|
@ -764,7 +790,8 @@ static string path_source_handle_preprocessor(const string &preprocessor_line,
|
||||||
if (path_read_text(filepath, text)) {
|
if (path_read_text(filepath, text)) {
|
||||||
text = path_source_replace_includes_recursive(text, filepath, state);
|
text = path_source_replace_includes_recursive(text, filepath, state);
|
||||||
/* Use line directives for better error messages. */
|
/* Use line directives for better error messages. */
|
||||||
return "\n" + text + "\n";
|
result = line_directive(*state, filepath, 1) + "\n" + text + "\n" +
|
||||||
|
line_directive(*state, source_filepath, line_number + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -813,7 +840,7 @@ static string path_source_replace_includes_recursive(const string &_source,
|
||||||
const size_t source_length = source.length();
|
const size_t source_length = source.length();
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
/* Information about where we are in the source. */
|
/* Information about where we are in the source. */
|
||||||
size_t column_number = 1;
|
size_t line_number = 0, column_number = 1;
|
||||||
/* Currently gathered non-preprocessor token.
|
/* Currently gathered non-preprocessor token.
|
||||||
* Store as start/length rather than token itself to avoid overhead of
|
* Store as start/length rather than token itself to avoid overhead of
|
||||||
* memory re-allocations on each character concatenation.
|
* memory re-allocations on each character concatenation.
|
||||||
|
@ -833,7 +860,8 @@ static string path_source_replace_includes_recursive(const string &_source,
|
||||||
|
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
if (inside_preprocessor) {
|
if (inside_preprocessor) {
|
||||||
string block = path_source_handle_preprocessor(preprocessor_line, source_filepath, state);
|
string block = path_source_handle_preprocessor(
|
||||||
|
preprocessor_line, source_filepath, line_number, state);
|
||||||
|
|
||||||
if (!block.empty()) {
|
if (!block.empty()) {
|
||||||
result += block;
|
result += block;
|
||||||
|
@ -846,6 +874,7 @@ static string path_source_replace_includes_recursive(const string &_source,
|
||||||
preprocessor_line = "";
|
preprocessor_line = "";
|
||||||
}
|
}
|
||||||
column_number = 0;
|
column_number = 0;
|
||||||
|
++line_number;
|
||||||
}
|
}
|
||||||
else if (ch == '#' && column_number == 1 && !inside_preprocessor) {
|
else if (ch == '#' && column_number == 1 && !inside_preprocessor) {
|
||||||
/* Append all possible non-preprocessor token to the result. */
|
/* Append all possible non-preprocessor token to the result. */
|
||||||
|
@ -873,7 +902,8 @@ static string path_source_replace_includes_recursive(const string &_source,
|
||||||
result.append(source, token_start, token_length);
|
result.append(source, token_start, token_length);
|
||||||
}
|
}
|
||||||
if (inside_preprocessor) {
|
if (inside_preprocessor) {
|
||||||
result += path_source_handle_preprocessor(preprocessor_line, source_filepath, state);
|
result += path_source_handle_preprocessor(
|
||||||
|
preprocessor_line, source_filepath, line_number, state);
|
||||||
}
|
}
|
||||||
/* Store result for further reuse. */
|
/* Store result for further reuse. */
|
||||||
state->processed_files[source_filepath] = result;
|
state->processed_files[source_filepath] = result;
|
||||||
|
|
|
@ -1198,6 +1198,7 @@ def km_property_editor(_params):
|
||||||
("object.modifier_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
|
("object.modifier_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
|
||||||
("object.modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
|
("object.modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
|
||||||
("object.modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None),
|
("object.modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None),
|
||||||
|
("object.add_modifier_menu", {"type": 'A', "value": 'PRESS', "shift": True}, None),
|
||||||
("object.modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}),
|
("object.modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}),
|
||||||
# Grease pencil modifier panels
|
# Grease pencil modifier panels
|
||||||
("object.gpencil_modifier_remove",
|
("object.gpencil_modifier_remove",
|
||||||
|
|
|
@ -230,6 +230,7 @@ class NewGeometryNodesModifier(Operator):
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
group = geometry_node_group_empty_new()
|
group = geometry_node_group_empty_new()
|
||||||
|
group.is_modifier = True
|
||||||
modifier.node_group = group
|
modifier.node_group = group
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
@ -257,6 +258,7 @@ class NewGeometryNodeTreeAssign(Operator):
|
||||||
if not modifier:
|
if not modifier:
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
group = geometry_node_group_empty_new()
|
group = geometry_node_group_empty_new()
|
||||||
|
group.is_modifier = True
|
||||||
modifier.node_group = group
|
modifier.node_group = group
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
from bpy.types import Panel
|
import bpy
|
||||||
|
from bpy.types import Panel, Menu, Operator
|
||||||
|
|
||||||
|
|
||||||
class ModifierButtonsPanel:
|
class ModifierButtonsPanel:
|
||||||
|
@ -22,10 +23,151 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||||
|
|
||||||
def draw(self, _context):
|
def draw(self, _context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.operator_menu_enum("object.modifier_add", "type")
|
layout.operator("wm.call_menu", text="Add Modifier", icon='ADD').name="OBJECT_MT_modifier_add"
|
||||||
layout.template_modifiers()
|
layout.template_modifiers()
|
||||||
|
|
||||||
|
|
||||||
|
class OBJECT_MT_modifier_add(Menu):
|
||||||
|
bl_label = "Add Modifier"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
ob_type = context.object.type
|
||||||
|
geometry_nodes_supported = ob_type in {'MESH', 'CURVE', 'CURVES', 'FONT', 'SURFACE', 'VOLUME', 'POINTCLOUD'}
|
||||||
|
if geometry_nodes_supported:
|
||||||
|
layout.operator("object.modifier_add", text="Empty Modifier").type='NODES'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.menu("OBJECT_MT_modifier_add_edit")
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME'}:
|
||||||
|
layout.menu("OBJECT_MT_modifier_add_generate")
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'VOLUME'}:
|
||||||
|
layout.menu("OBJECT_MT_modifier_add_deform")
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.menu("OBJECT_MT_modifier_add_physics")
|
||||||
|
|
||||||
|
if geometry_nodes_supported:
|
||||||
|
layout.menu_contents("OBJECT_MT_modifier_add_root_catalogs")
|
||||||
|
|
||||||
|
|
||||||
|
class OBJECT_MT_modifier_add_edit(Menu):
|
||||||
|
bl_label = "Edit"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
ob_type = context.object.type
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Data Transfer", icon='MOD_DATA_TRANSFER').type='DATA_TRANSFER'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Mesh Cache", icon='MOD_MESHDEFORM').type='MESH_CACHE'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Mesh Sequence Cache", icon='MOD_MESHDEFORM').type='MESH_SEQUENCE_CACHE'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Normal Edit", icon='MOD_NORMALEDIT').type='NORMAL_EDIT'
|
||||||
|
layout.operator("object.modifier_add", text="Weighted Normal", icon='MOD_NORMALEDIT').type='WEIGHTED_NORMAL'
|
||||||
|
layout.operator("object.modifier_add", text="UV Project", icon='MOD_UVPROJECT').type='UV_PROJECT'
|
||||||
|
layout.operator("object.modifier_add", text="UV Warp", icon='MOD_UVPROJECT').type='UV_WARP'
|
||||||
|
layout.operator("object.modifier_add", text="Vertex Weight Edit", icon='MOD_VERTEX_WEIGHT').type='VERTEX_WEIGHT_EDIT'
|
||||||
|
layout.operator("object.modifier_add", text="Vertex Weight Mix", icon='MOD_VERTEX_WEIGHT').type='VERTEX_WEIGHT_MIX'
|
||||||
|
layout.operator("object.modifier_add", text="Vertex Weight Proximity", icon='MOD_VERTEX_WEIGHT').type='VERTEX_WEIGHT_PROXIMITY'
|
||||||
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
||||||
|
|
||||||
|
|
||||||
|
class OBJECT_MT_modifier_add_generate(Menu):
|
||||||
|
bl_label = "Generate"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
ob_type = context.object.type
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Array", icon='MOD_ARRAY').type='ARRAY'
|
||||||
|
layout.operator("object.modifier_add", text="Bevel", icon='MOD_BEVEL').type='BEVEL'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Boolean", icon='MOD_BOOLEAN').type='BOOLEAN'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Build", icon='MOD_BUILD').type='BUILD'
|
||||||
|
layout.operator("object.modifier_add", text="Decimate", icon='MOD_DECIM').type='DECIMATE'
|
||||||
|
layout.operator("object.modifier_add", text="Edge Split", icon='MOD_EDGESPLIT').type='EDGE_SPLIT'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Mask", icon='MOD_MASK').type='MASK'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Mirror", icon='MOD_MIRROR').type='MIRROR'
|
||||||
|
if ob_type == 'VOLUME':
|
||||||
|
layout.operator("object.modifier_add", text="Mesh to Volume", icon='VOLUME_DATA').type='MESH_TO_VOLUME'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Multiresolution", icon='MOD_MULTIRES').type='MULTIRES'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Remesh", icon='MOD_REMESH').type='REMESH'
|
||||||
|
layout.operator("object.modifier_add", text="Screw", icon='MOD_SCREW').type='SCREW'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Skin", icon='MOD_SKIN').type='SKIN'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Solidify", icon='MOD_SOLIDIFY').type='SOLIDIFY'
|
||||||
|
layout.operator("object.modifier_add", text="Subdivision Surface", icon='MOD_SUBSURF').type='SUBSURF'
|
||||||
|
layout.operator("object.modifier_add", text="Triangulate", icon='MOD_TRIANGULATE').type='TRIANGULATE'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Volume to Mesh", icon='VOLUME_DATA').type='VOLUME_TO_MESH'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Weld", icon='AUTOMERGE_OFF').type='WELD'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Wireframe", icon='MOD_WIREFRAME').type='WIREFRAME'
|
||||||
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
||||||
|
|
||||||
|
|
||||||
|
class OBJECT_MT_modifier_add_deform(Menu):
|
||||||
|
bl_label = "Deform"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
ob_type = context.object.type
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Armature", icon='MOD_ARMATURE').type='ARMATURE'
|
||||||
|
layout.operator("object.modifier_add", text="Cast", icon='MOD_CAST').type='CAST'
|
||||||
|
layout.operator("object.modifier_add", text="Curve", icon='MOD_CURVE').type='CURVE'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Displace", icon='MOD_DISPLACE').type='DISPLACE'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Hook", icon='HOOK').type='HOOK'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Laplacian Deform", icon='MOD_MESHDEFORM').type='LAPLACIANDEFORM'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Lattice", icon='MOD_LATTICE').type='LATTICE'
|
||||||
|
layout.operator("object.modifier_add", text="Mesh Deform", icon='MOD_MESHDEFORM').type='MESH_DEFORM'
|
||||||
|
layout.operator("object.modifier_add", text="Shrinkwrap", icon='MOD_SHRINKWRAP').type='SHRINKWRAP'
|
||||||
|
layout.operator("object.modifier_add", text="Simple Deform", icon='MOD_SIMPLEDEFORM').type='SIMPLE_DEFORM'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Smooth", icon='MOD_SMOOTH').type='SMOOTH'
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Smooth Corrective", icon='MOD_SMOOTH').type='CORRECTIVE_SMOOTH'
|
||||||
|
layout.operator("object.modifier_add", text="Smooth Laplacian", icon='MOD_SMOOTH').type='LAPLACIANSMOOTH'
|
||||||
|
layout.operator("object.modifier_add", text="Surface Deform", icon='MOD_MESHDEFORM').type='SURFACE_DEFORM'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Warp", icon='MOD_WARP').type='WARP'
|
||||||
|
layout.operator("object.modifier_add", text="Wave", icon='MOD_WAVE').type='WAVE'
|
||||||
|
if ob_type == 'VOLUME':
|
||||||
|
layout.operator("object.modifier_add", text="Volume Displace", icon='VOLUME_DATA').type='VOLUME_DISPLACE'
|
||||||
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
||||||
|
|
||||||
|
|
||||||
|
class OBJECT_MT_modifier_add_physics(Menu):
|
||||||
|
bl_label = "Physics"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
ob_type = context.object.type
|
||||||
|
if ob_type == 'MESH':
|
||||||
|
layout.operator("object.modifier_add", text="Cloth", icon='MOD_CLOTH').type='CLOTH'
|
||||||
|
layout.operator("object.modifier_add", text="Collision", icon='MOD_PHYSICS').type='COLLISION'
|
||||||
|
layout.operator("object.modifier_add", text="Dynamic Paint", icon='MOD_DYNAMICPAINT').type='DYNAMIC_PAINT'
|
||||||
|
layout.operator("object.modifier_add", text="Explode", icon='MOD_EXPLODE').type='EXPLODE'
|
||||||
|
layout.operator("object.modifier_add", text="Fluid", icon='MOD_FLUIDSIM').type='FLUID'
|
||||||
|
layout.operator("object.modifier_add", text="Ocean", icon='MOD_OCEAN').type='OCEAN'
|
||||||
|
layout.operator("object.modifier_add", text="Particle Instance", icon='MOD_PARTICLE_INSTANCE').type='PARTICLE_INSTANCE'
|
||||||
|
layout.operator("object.modifier_add", text="Particle System", icon='MOD_PARTICLES').type='PARTICLE_SYSTEM'
|
||||||
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
||||||
|
layout.operator("object.modifier_add", text="Soft Body", icon='MOD_SOFT').type='SOFT_BODY'
|
||||||
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
||||||
|
|
||||||
|
|
||||||
class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
|
class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
|
||||||
bl_label = "Modifiers"
|
bl_label = "Modifiers"
|
||||||
|
|
||||||
|
@ -40,9 +182,28 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
|
||||||
layout.template_grease_pencil_modifiers()
|
layout.template_grease_pencil_modifiers()
|
||||||
|
|
||||||
|
|
||||||
|
class AddModifierMenu(Operator):
|
||||||
|
bl_idname = "object.add_modifier_menu"
|
||||||
|
bl_label = "Add Modifier"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
space = context.space_data
|
||||||
|
return space and space.context == "MODIFIER"
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return bpy.ops.wm.call_menu(name="OBJECT_MT_modifier_add")
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
DATA_PT_modifiers,
|
DATA_PT_modifiers,
|
||||||
|
OBJECT_MT_modifier_add,
|
||||||
|
OBJECT_MT_modifier_add_edit,
|
||||||
|
OBJECT_MT_modifier_add_generate,
|
||||||
|
OBJECT_MT_modifier_add_deform,
|
||||||
|
OBJECT_MT_modifier_add_physics,
|
||||||
DATA_PT_gpencil_modifiers,
|
DATA_PT_gpencil_modifiers,
|
||||||
|
AddModifierMenu,
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == "__main__": # only for live edit.
|
if __name__ == "__main__": # only for live edit.
|
||||||
|
|
|
@ -101,6 +101,7 @@ class GRAPH_PT_filters(DopesheetFilterPopoverBase, Panel):
|
||||||
layout.separator()
|
layout.separator()
|
||||||
DopesheetFilterPopoverBase.draw_standard_filters(context, layout)
|
DopesheetFilterPopoverBase.draw_standard_filters(context, layout)
|
||||||
|
|
||||||
|
|
||||||
class GRAPH_PT_snapping(Panel):
|
class GRAPH_PT_snapping(Panel):
|
||||||
bl_space_type = 'GRAPH_EDITOR'
|
bl_space_type = 'GRAPH_EDITOR'
|
||||||
bl_region_type = 'HEADER'
|
bl_region_type = 'HEADER'
|
||||||
|
|
|
@ -160,6 +160,8 @@ class NODE_HT_header(Header):
|
||||||
row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign")
|
row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign")
|
||||||
else:
|
else:
|
||||||
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
|
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
|
||||||
|
if snode.node_tree and snode.node_tree.asset_data:
|
||||||
|
layout.popover(panel="NODE_PT_geometry_node_asset_traits")
|
||||||
else:
|
else:
|
||||||
layout.template_ID(snode, "node_tree", new="node.new_geometry_node_group_tool")
|
layout.template_ID(snode, "node_tree", new="node.new_geometry_node_group_tool")
|
||||||
if snode.node_tree and snode.node_tree.asset_data:
|
if snode.node_tree and snode.node_tree.asset_data:
|
||||||
|
@ -448,18 +450,21 @@ class NODE_PT_geometry_node_asset_traits(Panel):
|
||||||
snode = context.space_data
|
snode = context.space_data
|
||||||
group = snode.node_tree
|
group = snode.node_tree
|
||||||
|
|
||||||
col = layout.column(heading="Type")
|
if snode.geometry_nodes_type == 'MODIFIER':
|
||||||
col.prop(group, "is_tool")
|
layout.prop(group, "is_modifier")
|
||||||
col = layout.column(heading="Mode")
|
else:
|
||||||
col.active = group.is_tool
|
col = layout.column(heading="Type")
|
||||||
col.prop(group, "is_mode_edit")
|
col.prop(group, "is_tool")
|
||||||
col.prop(group, "is_mode_sculpt")
|
col = layout.column(heading="Mode")
|
||||||
col = layout.column(heading="Geometry")
|
col.active = group.is_tool
|
||||||
col.active = group.is_tool
|
col.prop(group, "is_mode_edit")
|
||||||
col.prop(group, "is_type_mesh")
|
col.prop(group, "is_mode_sculpt")
|
||||||
col.prop(group, "is_type_curve")
|
col = layout.column(heading="Geometry")
|
||||||
if context.preferences.experimental.use_new_point_cloud_type:
|
col.active = group.is_tool
|
||||||
col.prop(group, "is_type_point_cloud")
|
col.prop(group, "is_type_mesh")
|
||||||
|
col.prop(group, "is_type_curve")
|
||||||
|
if context.preferences.experimental.use_new_point_cloud_type:
|
||||||
|
col.prop(group, "is_type_point_cloud")
|
||||||
|
|
||||||
|
|
||||||
class NODE_PT_node_color_presets(PresetPanel, Panel):
|
class NODE_PT_node_color_presets(PresetPanel, Panel):
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "MEM_guardedalloc.h"
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
#include "BKE_animsys.h"
|
#include "BKE_animsys.h"
|
||||||
|
#include "BKE_idprop.h"
|
||||||
|
|
||||||
#include "ANIM_armature_iter.hh"
|
#include "ANIM_armature_iter.hh"
|
||||||
#include "ANIM_bone_collections.h"
|
#include "ANIM_bone_collections.h"
|
||||||
|
@ -36,13 +37,14 @@ namespace {
|
||||||
/** Default flags for new bone collections. */
|
/** Default flags for new bone collections. */
|
||||||
constexpr eBoneCollection_Flag default_flags = BONE_COLLECTION_VISIBLE |
|
constexpr eBoneCollection_Flag default_flags = BONE_COLLECTION_VISIBLE |
|
||||||
BONE_COLLECTION_SELECTABLE;
|
BONE_COLLECTION_SELECTABLE;
|
||||||
|
constexpr auto bonecoll_default_name = "Bones";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BoneCollection *ANIM_bonecoll_new(const char *name)
|
BoneCollection *ANIM_bonecoll_new(const char *name)
|
||||||
{
|
{
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
/* Use a default name if no name was given. */
|
/* Use a default name if no name was given. */
|
||||||
name = "Bones";
|
name = bonecoll_default_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: the collection name may change after the collection is added to an
|
/* Note: the collection name may change after the collection is added to an
|
||||||
|
@ -62,6 +64,9 @@ void ANIM_bonecoll_free(BoneCollection *bcoll)
|
||||||
BLI_assert_msg(BLI_listbase_is_empty(&bcoll->bones),
|
BLI_assert_msg(BLI_listbase_is_empty(&bcoll->bones),
|
||||||
"bone collection still has bones assigned to it, will cause dangling pointers in "
|
"bone collection still has bones assigned to it, will cause dangling pointers in "
|
||||||
"bone runtime data");
|
"bone runtime data");
|
||||||
|
if (bcoll->prop) {
|
||||||
|
IDP_FreeProperty(bcoll->prop);
|
||||||
|
}
|
||||||
MEM_delete(bcoll);
|
MEM_delete(bcoll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +97,7 @@ static void bonecoll_ensure_name_unique(bArmature *armature, BoneCollection *bco
|
||||||
{
|
{
|
||||||
BLI_uniquename(&armature->collections,
|
BLI_uniquename(&armature->collections,
|
||||||
bcoll,
|
bcoll,
|
||||||
"Bones",
|
bonecoll_default_name,
|
||||||
'.',
|
'.',
|
||||||
offsetof(BoneCollection, name),
|
offsetof(BoneCollection, name),
|
||||||
sizeof(bcoll->name));
|
sizeof(bcoll->name));
|
||||||
|
|
|
@ -417,13 +417,13 @@ Icon *BKE_icon_get(const int icon_id)
|
||||||
bool BKE_icon_is_preview(const int icon_id)
|
bool BKE_icon_is_preview(const int icon_id)
|
||||||
{
|
{
|
||||||
const Icon *icon = BKE_icon_get(icon_id);
|
const Icon *icon = BKE_icon_get(icon_id);
|
||||||
return icon->obj_type == ICON_DATA_PREVIEW;
|
return icon != nullptr && icon->obj_type == ICON_DATA_PREVIEW;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BKE_icon_is_image(const int icon_id)
|
bool BKE_icon_is_image(const int icon_id)
|
||||||
{
|
{
|
||||||
const Icon *icon = BKE_icon_get(icon_id);
|
const Icon *icon = BKE_icon_get(icon_id);
|
||||||
return icon->obj_type == ICON_DATA_IMBUF;
|
return icon != nullptr && icon->obj_type == ICON_DATA_IMBUF;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_icon_set(const int icon_id, Icon *icon)
|
void BKE_icon_set(const int icon_id, Icon *icon)
|
||||||
|
|
|
@ -186,6 +186,21 @@ static void version_bonegroups_to_bonecollections(Main *bmain)
|
||||||
/* Convert the bone groups on a bone-by-bone basis. */
|
/* Convert the bone groups on a bone-by-bone basis. */
|
||||||
bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
|
bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
|
||||||
bPose *pose = ob->pose;
|
bPose *pose = ob->pose;
|
||||||
|
|
||||||
|
blender::Map<const bActionGroup *, BoneCollection *> collections_by_group;
|
||||||
|
/* Convert all bone groups, regardless of whether they contain any bones. */
|
||||||
|
LISTBASE_FOREACH (bActionGroup *, bgrp, &pose->agroups) {
|
||||||
|
BoneCollection *bcoll = ANIM_armature_bonecoll_new(arm, bgrp->name);
|
||||||
|
collections_by_group.add_new(bgrp, bcoll);
|
||||||
|
|
||||||
|
/* Before now, bone visibility was determined by armature layers, and bone
|
||||||
|
* groups did not have any impact on this. To retain the behavior, that
|
||||||
|
* hiding all layers a bone is on hides the bone, the
|
||||||
|
* bone-group-collections should be created hidden. */
|
||||||
|
ANIM_bonecoll_hide(bcoll);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assign the bones to their bone group based collection. */
|
||||||
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
|
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
|
||||||
/* Find the bone group of this pose channel. */
|
/* Find the bone group of this pose channel. */
|
||||||
const bActionGroup *bgrp = (const bActionGroup *)BLI_findlink(&pose->agroups,
|
const bActionGroup *bgrp = (const bActionGroup *)BLI_findlink(&pose->agroups,
|
||||||
|
@ -194,15 +209,8 @@ static void version_bonegroups_to_bonecollections(Main *bmain)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get or create the bone collection. */
|
|
||||||
BoneCollection *bcoll = ANIM_armature_bonecoll_get_by_name(arm, bgrp->name);
|
|
||||||
if (!bcoll) {
|
|
||||||
bcoll = ANIM_armature_bonecoll_new(arm, bgrp->name);
|
|
||||||
|
|
||||||
ANIM_bonecoll_hide(bcoll);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assign the bone. */
|
/* Assign the bone. */
|
||||||
|
BoneCollection *bcoll = collections_by_group.lookup(bgrp);
|
||||||
ANIM_armature_bonecoll_assign(bcoll, pchan->bone);
|
ANIM_armature_bonecoll_assign(bcoll, pchan->bone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -969,7 +977,7 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||||
scene->eevee.gi_irradiance_pool_size = 16;
|
scene->eevee.gi_irradiance_pool_size = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||||
scene->toolsettings->snap_flag_anim |= SCE_SNAP;
|
scene->toolsettings->snap_flag_anim |= SCE_SNAP;
|
||||||
scene->toolsettings->snap_anim_mode |= SCE_SNAP_TO_FRAME;
|
scene->toolsettings->snap_anim_mode |= SCE_SNAP_TO_FRAME;
|
||||||
|
|
|
@ -104,6 +104,8 @@ class Instance {
|
||||||
return scene_state.material_override;
|
return scene_state.material_override;
|
||||||
case V3D_SHADING_VERTEX_COLOR:
|
case V3D_SHADING_VERTEX_COLOR:
|
||||||
return scene_state.material_attribute_color;
|
return scene_state.material_attribute_color;
|
||||||
|
case V3D_SHADING_TEXTURE_COLOR:
|
||||||
|
ATTR_FALLTHROUGH;
|
||||||
case V3D_SHADING_MATERIAL_COLOR:
|
case V3D_SHADING_MATERIAL_COLOR:
|
||||||
if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, slot)) {
|
if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, slot)) {
|
||||||
return Material(*_mat);
|
return Material(*_mat);
|
||||||
|
|
|
@ -1027,11 +1027,9 @@ static bool acf_fcurve_name_prop(bAnimListElem *ale, PointerRNA *r_ptr, Property
|
||||||
|
|
||||||
/* check if some setting exists for this channel */
|
/* check if some setting exists for this channel */
|
||||||
static bool acf_fcurve_setting_valid(bAnimContext *ac,
|
static bool acf_fcurve_setting_valid(bAnimContext *ac,
|
||||||
bAnimListElem *ale,
|
bAnimListElem * /*ale*/,
|
||||||
eAnimChannel_Settings setting)
|
eAnimChannel_Settings setting)
|
||||||
{
|
{
|
||||||
FCurve *fcu = (FCurve *)ale->data;
|
|
||||||
|
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
/* unsupported */
|
/* unsupported */
|
||||||
case ACHANNEL_SETTING_SOLO: /* Solo Flag is only for NLA */
|
case ACHANNEL_SETTING_SOLO: /* Solo Flag is only for NLA */
|
||||||
|
@ -1039,15 +1037,6 @@ static bool acf_fcurve_setting_valid(bAnimContext *ac,
|
||||||
case ACHANNEL_SETTING_PINNED: /* This is only for NLA Actions */
|
case ACHANNEL_SETTING_PINNED: /* This is only for NLA Actions */
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* conditionally available */
|
|
||||||
case ACHANNEL_SETTING_PROTECT: /* Protection is only valid when there's keyframes */
|
|
||||||
if (fcu->bezt) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false; /* NOTE: in this special case, we need to draw ICON_ZOOMOUT */
|
|
||||||
}
|
|
||||||
|
|
||||||
case ACHANNEL_SETTING_VISIBLE: /* Only available in Graph Editor */
|
case ACHANNEL_SETTING_VISIBLE: /* Only available in Graph Editor */
|
||||||
return (ac->spacetype == SPACE_GRAPH);
|
return (ac->spacetype == SPACE_GRAPH);
|
||||||
|
|
||||||
|
@ -5613,7 +5602,17 @@ void ANIM_channel_draw_widgets(const bContext *C,
|
||||||
/* protect... */
|
/* protect... */
|
||||||
if (acf->has_setting(ac, ale, ACHANNEL_SETTING_PROTECT)) {
|
if (acf->has_setting(ac, ale, ACHANNEL_SETTING_PROTECT)) {
|
||||||
offset -= ICON_WIDTH;
|
offset -= ICON_WIDTH;
|
||||||
draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_PROTECT);
|
if (ale->type == ANIMTYPE_FCURVE) {
|
||||||
|
FCurve *fcu = static_cast<FCurve *>(ale->data);
|
||||||
|
/* Don't draw lock icon when curve is baked.
|
||||||
|
* Still using the offset so icons are aligned. */
|
||||||
|
if (fcu->bezt) {
|
||||||
|
draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_PROTECT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_PROTECT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* mute... */
|
/* mute... */
|
||||||
if (acf->has_setting(ac, ale, ACHANNEL_SETTING_MUTE)) {
|
if (acf->has_setting(ac, ale, ACHANNEL_SETTING_MUTE)) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ set(SRC
|
||||||
intern/asset_library_reference_enum.cc
|
intern/asset_library_reference_enum.cc
|
||||||
intern/asset_list.cc
|
intern/asset_list.cc
|
||||||
intern/asset_mark_clear.cc
|
intern/asset_mark_clear.cc
|
||||||
|
intern/asset_menu_utils.cc
|
||||||
intern/asset_ops.cc
|
intern/asset_ops.cc
|
||||||
intern/asset_shelf.cc
|
intern/asset_shelf.cc
|
||||||
intern/asset_shelf_asset_view.cc
|
intern/asset_shelf_asset_view.cc
|
||||||
|
|
|
@ -26,10 +26,6 @@
|
||||||
struct AssetLibrary;
|
struct AssetLibrary;
|
||||||
struct bScreen;
|
struct bScreen;
|
||||||
|
|
||||||
namespace blender::asset_system {
|
|
||||||
class AssetCatalogTreeItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if the catalogs of \a library are allowed to be editable, or if the UI should forbid
|
* Returns if the catalogs of \a library are allowed to be editable, or if the UI should forbid
|
||||||
* edits.
|
* edits.
|
||||||
|
@ -59,16 +55,3 @@ void ED_asset_catalog_move(
|
||||||
AssetLibrary *library,
|
AssetLibrary *library,
|
||||||
blender::asset_system::CatalogID src_catalog_id,
|
blender::asset_system::CatalogID src_catalog_id,
|
||||||
std::optional<blender::asset_system::CatalogID> dst_parent_catalog_id = std::nullopt);
|
std::optional<blender::asset_system::CatalogID> dst_parent_catalog_id = std::nullopt);
|
||||||
|
|
||||||
namespace blender::ed::asset {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some code needs to pass catalog paths to context and for this they need persistent pointers to
|
|
||||||
* the paths. Rather than keeping some local path storage, get a pointer into the asset system
|
|
||||||
* directly, which is persistent until the library is reloaded and can safely be held by context.
|
|
||||||
*/
|
|
||||||
PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen,
|
|
||||||
const asset_system::AssetLibrary &library,
|
|
||||||
const asset_system::AssetCatalogTreeItem &item);
|
|
||||||
|
|
||||||
} // namespace blender::ed::asset
|
|
||||||
|
|
|
@ -47,13 +47,3 @@ void ED_asset_handle_get_full_library_path(
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
namespace blender::ed::asset {
|
|
||||||
|
|
||||||
PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -206,23 +206,3 @@ bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved()
|
||||||
{
|
{
|
||||||
return asset_system::AssetLibrary::save_catalogs_when_file_is_saved;
|
return asset_system::AssetLibrary::save_catalogs_when_file_is_saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace blender::ed::asset {
|
|
||||||
|
|
||||||
PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen,
|
|
||||||
const asset_system::AssetLibrary &library,
|
|
||||||
const asset_system::AssetCatalogTreeItem &item)
|
|
||||||
{
|
|
||||||
const asset_system::AssetCatalog *catalog = library.catalog_service->find_catalog_by_path(
|
|
||||||
item.catalog_path());
|
|
||||||
if (!catalog) {
|
|
||||||
return PointerRNA_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const asset_system::AssetCatalogPath &path = catalog->path;
|
|
||||||
return {&const_cast<ID &>(owner_screen.id),
|
|
||||||
&RNA_AssetCatalogPath,
|
|
||||||
const_cast<asset_system::AssetCatalogPath *>(&path)};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace blender::ed::asset
|
|
||||||
|
|
|
@ -57,16 +57,3 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle,
|
||||||
|
|
||||||
BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX);
|
BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace blender::ed::asset {
|
|
||||||
|
|
||||||
PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset)
|
|
||||||
{
|
|
||||||
PointerRNA ptr{};
|
|
||||||
ptr.owner_id = nullptr;
|
|
||||||
ptr.type = &RNA_AssetRepresentation;
|
|
||||||
ptr.data = const_cast<asset_system::AssetRepresentation *>(asset);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace blender::ed::asset
|
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup edasset
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AS_asset_catalog.hh"
|
||||||
|
#include "AS_asset_catalog_tree.hh"
|
||||||
|
#include "AS_asset_library.hh"
|
||||||
|
#include "AS_asset_representation.hh"
|
||||||
|
|
||||||
|
#include "DNA_screen_types.h"
|
||||||
|
|
||||||
|
#include "BKE_asset.h"
|
||||||
|
#include "BKE_report.h"
|
||||||
|
|
||||||
|
#include "BLT_translation.h"
|
||||||
|
|
||||||
|
#include "WM_api.hh"
|
||||||
|
|
||||||
|
#include "RNA_access.hh"
|
||||||
|
#include "RNA_define.hh"
|
||||||
|
#include "RNA_enum_types.hh"
|
||||||
|
#include "RNA_prototypes.h"
|
||||||
|
|
||||||
|
#include "ED_asset_list.h"
|
||||||
|
#include "ED_asset_list.hh"
|
||||||
|
#include "ED_asset_menu_utils.hh"
|
||||||
|
|
||||||
|
#include "UI_interface.hh"
|
||||||
|
|
||||||
|
namespace blender::ed::asset {
|
||||||
|
|
||||||
|
void operator_asset_reference_props_register(StructRNA &srna)
|
||||||
|
{
|
||||||
|
PropertyRNA *prop;
|
||||||
|
prop = RNA_def_enum(&srna,
|
||||||
|
"asset_library_type",
|
||||||
|
rna_enum_aset_library_type_items,
|
||||||
|
ASSET_LIBRARY_LOCAL,
|
||||||
|
"Asset Library Type",
|
||||||
|
"");
|
||||||
|
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||||
|
prop = RNA_def_string(
|
||||||
|
&srna, "asset_library_identifier", nullptr, 0, "Asset Library Identifier", "");
|
||||||
|
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||||
|
prop = RNA_def_string(
|
||||||
|
&srna, "relative_asset_identifier", nullptr, 0, "Relative Asset Identifier", "");
|
||||||
|
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset,
|
||||||
|
PointerRNA &ptr)
|
||||||
|
{
|
||||||
|
AssetWeakReference *weak_ref = asset.make_weak_reference();
|
||||||
|
RNA_enum_set(&ptr, "asset_library_type", weak_ref->asset_library_type);
|
||||||
|
RNA_string_set(&ptr, "asset_library_identifier", weak_ref->asset_library_identifier);
|
||||||
|
RNA_string_set(&ptr, "relative_asset_identifier", weak_ref->relative_asset_identifier);
|
||||||
|
BKE_asset_weak_reference_free(&weak_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #AssetLibrary::resolve_asset_weak_reference_to_full_path() currently does not support local
|
||||||
|
* assets.
|
||||||
|
*/
|
||||||
|
static const asset_system::AssetRepresentation *get_local_asset_from_relative_identifier(
|
||||||
|
const bContext &C, const StringRefNull relative_identifier, ReportList *reports)
|
||||||
|
{
|
||||||
|
AssetLibraryReference library_ref{};
|
||||||
|
library_ref.type = ASSET_LIBRARY_LOCAL;
|
||||||
|
ED_assetlist_storage_fetch(&library_ref, &C);
|
||||||
|
ED_assetlist_ensure_previews_job(&library_ref, &C);
|
||||||
|
|
||||||
|
const asset_system::AssetRepresentation *matching_asset = nullptr;
|
||||||
|
ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) {
|
||||||
|
if (asset.get_identifier().library_relative_identifier() == relative_identifier) {
|
||||||
|
matching_asset = &asset;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reports && !matching_asset) {
|
||||||
|
if (ED_assetlist_is_loaded(&library_ref)) {
|
||||||
|
BKE_reportf(
|
||||||
|
reports, RPT_ERROR, "No asset found at path \"%s\"", relative_identifier.c_str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BKE_report(reports, RPT_WARNING, "Asset loading is unfinished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matching_asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const asset_system::AssetRepresentation *find_asset_from_weak_ref(
|
||||||
|
const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports)
|
||||||
|
{
|
||||||
|
if (weak_ref.asset_library_type == ASSET_LIBRARY_LOCAL) {
|
||||||
|
return get_local_asset_from_relative_identifier(
|
||||||
|
C, weak_ref.relative_asset_identifier, reports);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssetLibraryReference library_ref = asset_system::all_library_reference();
|
||||||
|
ED_assetlist_storage_fetch(&library_ref, &C);
|
||||||
|
ED_assetlist_ensure_previews_job(&library_ref, &C);
|
||||||
|
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
|
||||||
|
asset_system::all_library_reference());
|
||||||
|
if (!all_library) {
|
||||||
|
BKE_report(reports, RPT_WARNING, "Asset loading is unfinished");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string full_path = all_library->resolve_asset_weak_reference_to_full_path(weak_ref);
|
||||||
|
|
||||||
|
const asset_system::AssetRepresentation *matching_asset = nullptr;
|
||||||
|
ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) {
|
||||||
|
if (asset.get_identifier().full_path() == full_path) {
|
||||||
|
matching_asset = &asset;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reports && !matching_asset) {
|
||||||
|
if (ED_assetlist_is_loaded(&library_ref)) {
|
||||||
|
BKE_reportf(reports, RPT_ERROR, "No asset found at path \"%s\"", full_path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matching_asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const asset_system::AssetRepresentation *operator_asset_reference_props_get_asset_from_all_library(
|
||||||
|
const bContext &C, PointerRNA &ptr, ReportList *reports)
|
||||||
|
{
|
||||||
|
AssetWeakReference weak_ref{};
|
||||||
|
weak_ref.asset_library_type = RNA_enum_get(&ptr, "asset_library_type");
|
||||||
|
weak_ref.asset_library_identifier = RNA_string_get_alloc(
|
||||||
|
&ptr, "asset_library_identifier", nullptr, 0, nullptr);
|
||||||
|
weak_ref.relative_asset_identifier = RNA_string_get_alloc(
|
||||||
|
&ptr, "relative_asset_identifier", nullptr, 0, nullptr);
|
||||||
|
return find_asset_from_weak_ref(C, weak_ref, reports);
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen,
|
||||||
|
const asset_system::AssetLibrary &library,
|
||||||
|
const asset_system::AssetCatalogTreeItem &item)
|
||||||
|
{
|
||||||
|
const asset_system::AssetCatalog *catalog = library.catalog_service->find_catalog_by_path(
|
||||||
|
item.catalog_path());
|
||||||
|
if (!catalog) {
|
||||||
|
return PointerRNA_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const asset_system::AssetCatalogPath &path = catalog->path;
|
||||||
|
return {&const_cast<ID &>(owner_screen.id),
|
||||||
|
&RNA_AssetCatalogPath,
|
||||||
|
const_cast<asset_system::AssetCatalogPath *>(&path)};
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset)
|
||||||
|
{
|
||||||
|
PointerRNA ptr{};
|
||||||
|
ptr.owner_id = nullptr;
|
||||||
|
ptr.type = &RNA_AssetRepresentation;
|
||||||
|
ptr.data = const_cast<asset_system::AssetRepresentation *>(asset);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_menu_for_catalog(const bScreen &owner_screen,
|
||||||
|
const asset_system::AssetLibrary &library,
|
||||||
|
const asset_system::AssetCatalogTreeItem &item,
|
||||||
|
const StringRefNull menu_name,
|
||||||
|
uiLayout &layout)
|
||||||
|
{
|
||||||
|
PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(owner_screen, library, item);
|
||||||
|
if (path_ptr.data == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiLayout *col = uiLayoutColumn(&layout, false);
|
||||||
|
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
||||||
|
uiItemM(col, menu_name.c_str(), IFACE_(item.get_name().c_str()), ICON_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blender::ed::asset
|
|
@ -10,6 +10,7 @@
|
||||||
#include "AS_asset_library.hh"
|
#include "AS_asset_library.hh"
|
||||||
#include "AS_asset_representation.hh"
|
#include "AS_asset_representation.hh"
|
||||||
|
|
||||||
|
#include "BKE_asset.h"
|
||||||
#include "BKE_bpath.h"
|
#include "BKE_bpath.h"
|
||||||
#include "BKE_context.h"
|
#include "BKE_context.h"
|
||||||
#include "BKE_lib_id.h"
|
#include "BKE_lib_id.h"
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
|
|
||||||
#include "RNA_access.hh"
|
#include "RNA_access.hh"
|
||||||
#include "RNA_define.hh"
|
#include "RNA_define.hh"
|
||||||
|
#include "RNA_enum_types.hh"
|
||||||
#include "RNA_prototypes.h"
|
#include "RNA_prototypes.h"
|
||||||
|
|
||||||
#include "WM_api.hh"
|
#include "WM_api.hh"
|
||||||
|
|
|
@ -42,12 +42,12 @@
|
||||||
|
|
||||||
#include "RNA_access.hh"
|
#include "RNA_access.hh"
|
||||||
#include "RNA_define.hh"
|
#include "RNA_define.hh"
|
||||||
#include "RNA_enum_types.hh"
|
|
||||||
|
|
||||||
#include "UI_interface.hh"
|
#include "UI_interface.hh"
|
||||||
#include "UI_resources.hh"
|
#include "UI_resources.hh"
|
||||||
|
|
||||||
#include "ED_asset.hh"
|
#include "ED_asset.hh"
|
||||||
|
#include "ED_asset_menu_utils.hh"
|
||||||
#include "ED_geometry.hh"
|
#include "ED_geometry.hh"
|
||||||
#include "ED_mesh.hh"
|
#include "ED_mesh.hh"
|
||||||
|
|
||||||
|
@ -72,92 +72,10 @@ namespace blender::ed::geometry {
|
||||||
/** \name Operator
|
/** \name Operator
|
||||||
* \{ */
|
* \{ */
|
||||||
|
|
||||||
/**
|
|
||||||
* #AssetLibrary::resolve_asset_weak_reference_to_full_path() currently does not support local
|
|
||||||
* assets.
|
|
||||||
*/
|
|
||||||
static const asset_system::AssetRepresentation *get_local_asset_from_relative_identifier(
|
|
||||||
const bContext &C, const StringRefNull relative_identifier, ReportList *reports)
|
|
||||||
{
|
|
||||||
AssetLibraryReference library_ref{};
|
|
||||||
library_ref.type = ASSET_LIBRARY_LOCAL;
|
|
||||||
ED_assetlist_storage_fetch(&library_ref, &C);
|
|
||||||
ED_assetlist_ensure_previews_job(&library_ref, &C);
|
|
||||||
|
|
||||||
const asset_system::AssetRepresentation *matching_asset = nullptr;
|
|
||||||
ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) {
|
|
||||||
if (asset.get_identifier().library_relative_identifier() == relative_identifier) {
|
|
||||||
matching_asset = &asset;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (reports && !matching_asset) {
|
|
||||||
if (ED_assetlist_is_loaded(&library_ref)) {
|
|
||||||
BKE_reportf(
|
|
||||||
reports, RPT_ERROR, "No asset found at path \"%s\"", relative_identifier.c_str());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
BKE_report(reports, RPT_WARNING, "Asset loading is unfinished");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matching_asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const asset_system::AssetRepresentation *find_asset_from_weak_ref(
|
|
||||||
const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports)
|
|
||||||
{
|
|
||||||
if (weak_ref.asset_library_type == ASSET_LIBRARY_LOCAL) {
|
|
||||||
return get_local_asset_from_relative_identifier(
|
|
||||||
C, weak_ref.relative_asset_identifier, reports);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AssetLibraryReference library_ref = asset_system::all_library_reference();
|
|
||||||
ED_assetlist_storage_fetch(&library_ref, &C);
|
|
||||||
ED_assetlist_ensure_previews_job(&library_ref, &C);
|
|
||||||
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
|
|
||||||
asset_system::all_library_reference());
|
|
||||||
if (!all_library) {
|
|
||||||
BKE_report(reports, RPT_WARNING, "Asset loading is unfinished");
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string full_path = all_library->resolve_asset_weak_reference_to_full_path(weak_ref);
|
|
||||||
|
|
||||||
const asset_system::AssetRepresentation *matching_asset = nullptr;
|
|
||||||
ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) {
|
|
||||||
if (asset.get_identifier().full_path() == full_path) {
|
|
||||||
matching_asset = &asset;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (reports && !matching_asset) {
|
|
||||||
if (ED_assetlist_is_loaded(&library_ref)) {
|
|
||||||
BKE_reportf(reports, RPT_ERROR, "No asset found at path \"%s\"", full_path.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matching_asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \note Does not check asset type or meta data. */
|
|
||||||
static const asset_system::AssetRepresentation *get_asset(const bContext &C,
|
|
||||||
PointerRNA &ptr,
|
|
||||||
ReportList *reports)
|
|
||||||
{
|
|
||||||
AssetWeakReference weak_ref{};
|
|
||||||
weak_ref.asset_library_type = RNA_enum_get(&ptr, "asset_library_type");
|
|
||||||
weak_ref.asset_library_identifier = RNA_string_get_alloc(
|
|
||||||
&ptr, "asset_library_identifier", nullptr, 0, nullptr);
|
|
||||||
weak_ref.relative_asset_identifier = RNA_string_get_alloc(
|
|
||||||
&ptr, "relative_asset_identifier", nullptr, 0, nullptr);
|
|
||||||
return find_asset_from_weak_ref(C, weak_ref, reports);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
|
static const bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
|
||||||
{
|
{
|
||||||
const asset_system::AssetRepresentation *asset = get_asset(C, ptr, reports);
|
const asset_system::AssetRepresentation *asset =
|
||||||
|
asset::operator_asset_reference_props_get_asset_from_all_library(C, ptr, reports);
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -376,7 +294,8 @@ static std::string run_node_group_get_description(bContext *C,
|
||||||
wmOperatorType * /*ot*/,
|
wmOperatorType * /*ot*/,
|
||||||
PointerRNA *ptr)
|
PointerRNA *ptr)
|
||||||
{
|
{
|
||||||
const asset_system::AssetRepresentation *asset = get_asset(*C, *ptr, nullptr);
|
const asset_system::AssetRepresentation *asset =
|
||||||
|
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *ptr, nullptr);
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -534,20 +453,7 @@ void GEOMETRY_OT_execute_node_group(wmOperatorType *ot)
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
PropertyRNA *prop;
|
asset::operator_asset_reference_props_register(*ot->srna);
|
||||||
prop = RNA_def_enum(ot->srna,
|
|
||||||
"asset_library_type",
|
|
||||||
rna_enum_aset_library_type_items,
|
|
||||||
ASSET_LIBRARY_LOCAL,
|
|
||||||
"Asset Library Type",
|
|
||||||
"");
|
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
|
||||||
prop = RNA_def_string(
|
|
||||||
ot->srna, "asset_library_identifier", nullptr, 0, "Asset Library Identifier", "");
|
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
|
||||||
prop = RNA_def_string(
|
|
||||||
ot->srna, "relative_asset_identifier", nullptr, 0, "Relative Asset Identifier", "");
|
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
@ -706,7 +612,6 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu)
|
||||||
for (const asset_system::AssetRepresentation *asset : assets) {
|
for (const asset_system::AssetRepresentation *asset : assets) {
|
||||||
uiLayout *col = uiLayoutColumn(layout, false);
|
uiLayout *col = uiLayoutColumn(layout, false);
|
||||||
wmOperatorType *ot = WM_operatortype_find("GEOMETRY_OT_execute_node_group", true);
|
wmOperatorType *ot = WM_operatortype_find("GEOMETRY_OT_execute_node_group", true);
|
||||||
AssetWeakReference *weak_ref = asset->make_weak_reference();
|
|
||||||
PointerRNA props_ptr;
|
PointerRNA props_ptr;
|
||||||
uiItemFullO_ptr(col,
|
uiItemFullO_ptr(col,
|
||||||
ot,
|
ot,
|
||||||
|
@ -716,11 +621,7 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu)
|
||||||
WM_OP_INVOKE_DEFAULT,
|
WM_OP_INVOKE_DEFAULT,
|
||||||
UI_ITEM_NONE,
|
UI_ITEM_NONE,
|
||||||
&props_ptr);
|
&props_ptr);
|
||||||
RNA_enum_set(&props_ptr, "asset_library_type", weak_ref->asset_library_type);
|
asset::operator_asset_reference_props_set(*asset, props_ptr);
|
||||||
RNA_string_set(&props_ptr, "asset_library_identifier", weak_ref->asset_library_identifier);
|
|
||||||
RNA_string_set(&props_ptr, "relative_asset_identifier", weak_ref->relative_asset_identifier);
|
|
||||||
|
|
||||||
BKE_asset_weak_reference_free(&weak_ref);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
|
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
|
||||||
|
@ -729,18 +630,9 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &child_item) {
|
catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &item) {
|
||||||
PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(
|
asset::draw_menu_for_catalog(
|
||||||
screen, *all_library, child_item);
|
screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", *layout);
|
||||||
if (path_ptr.data == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uiLayout *col = uiLayoutColumn(layout, false);
|
|
||||||
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
|
||||||
uiItemM(col,
|
|
||||||
"GEO_MT_node_operator_catalog_assets",
|
|
||||||
IFACE_(child_item.get_name().c_str()),
|
|
||||||
ICON_NONE);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,17 +701,10 @@ void ui_template_node_operator_asset_root_items(uiLayout &layout, bContext &C)
|
||||||
eObjectMode(active_object->mode));
|
eObjectMode(active_object->mode));
|
||||||
|
|
||||||
tree->catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) {
|
tree->catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) {
|
||||||
if (builtin_menus.contains(item.get_name())) {
|
if (!builtin_menus.contains(item.get_name())) {
|
||||||
return;
|
asset::draw_menu_for_catalog(
|
||||||
|
screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", layout);
|
||||||
}
|
}
|
||||||
PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(screen, *all_library, item);
|
|
||||||
if (path_ptr.data == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uiLayout *col = uiLayoutColumn(&layout, false);
|
|
||||||
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
|
||||||
const char *text = IFACE_(item.get_name().c_str());
|
|
||||||
uiItemM(col, "GEO_MT_node_operator_catalog_assets", text, ICON_NONE);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup edasset
|
||||||
|
*
|
||||||
|
* Code for dealing with dynamic asset menus and passing assets to operators with RNA properties.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BLI_string_ref.hh"
|
||||||
|
|
||||||
|
#include "RNA_types.hh"
|
||||||
|
|
||||||
|
struct AssetLibrary;
|
||||||
|
struct bScreen;
|
||||||
|
struct uiLayout;
|
||||||
|
|
||||||
|
namespace blender::asset_system {
|
||||||
|
class AssetCatalogTreeItem;
|
||||||
|
class AssetLibrary;
|
||||||
|
class AssetRepresentation;
|
||||||
|
} // namespace blender::asset_system
|
||||||
|
|
||||||
|
namespace blender::ed::asset {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some code needs to pass catalog paths to context and for this they need persistent pointers to
|
||||||
|
* the paths. Rather than keeping some local path storage, get a pointer into the asset system
|
||||||
|
* directly, which is persistent until the library is reloaded and can safely be held by context.
|
||||||
|
*/
|
||||||
|
PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen,
|
||||||
|
const asset_system::AssetLibrary &library,
|
||||||
|
const asset_system::AssetCatalogTreeItem &item);
|
||||||
|
|
||||||
|
void draw_menu_for_catalog(const bScreen &owner_screen,
|
||||||
|
const asset_system::AssetLibrary &library,
|
||||||
|
const asset_system::AssetCatalogTreeItem &item,
|
||||||
|
StringRefNull menu_name,
|
||||||
|
uiLayout &layout);
|
||||||
|
|
||||||
|
PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset);
|
||||||
|
|
||||||
|
void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset,
|
||||||
|
PointerRNA &ptr);
|
||||||
|
void operator_asset_reference_props_register(StructRNA &srna);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all asset libraries to find an asset from the #operator_asset_reference_props_register
|
||||||
|
* properties. The loading happens in the background, so there may be no result immediately. In
|
||||||
|
* that case an "Asset loading is unfinished" report is added.
|
||||||
|
*
|
||||||
|
* \note Does not check asset type or meta data.
|
||||||
|
*/
|
||||||
|
const asset_system::AssetRepresentation *operator_asset_reference_props_get_asset_from_all_library(
|
||||||
|
const bContext &C, PointerRNA &ptr, ReportList *reports);
|
||||||
|
|
||||||
|
} // namespace blender::ed::asset
|
|
@ -9,6 +9,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BLI_compiler_attrs.h"
|
#include "BLI_compiler_attrs.h"
|
||||||
|
#include "BLI_string_ref.hh"
|
||||||
|
|
||||||
#include "DNA_object_enums.h"
|
#include "DNA_object_enums.h"
|
||||||
#include "DNA_userdef_enums.h"
|
#include "DNA_userdef_enums.h"
|
||||||
#include "DNA_windowmanager_types.h"
|
#include "DNA_windowmanager_types.h"
|
||||||
|
@ -636,3 +638,9 @@ void ED_object_data_xform_by_mat4(XFormObjectData *xod, const float mat[4][4]);
|
||||||
|
|
||||||
void ED_object_data_xform_restore(XFormObjectData *xod);
|
void ED_object_data_xform_restore(XFormObjectData *xod);
|
||||||
void ED_object_data_xform_tag_update(XFormObjectData *xod);
|
void ED_object_data_xform_tag_update(XFormObjectData *xod);
|
||||||
|
|
||||||
|
namespace blender::ed::object {
|
||||||
|
|
||||||
|
void ui_template_modifier_asset_menu_items(uiLayout &layout, bContext &C, StringRef catalog_path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
set(INC
|
set(INC
|
||||||
../include
|
../include
|
||||||
|
../../asset_system
|
||||||
../../blenfont
|
../../blenfont
|
||||||
../../blenkernel
|
../../blenkernel
|
||||||
../../blentranslation
|
../../blentranslation
|
||||||
|
@ -30,6 +31,7 @@ set(INC_SYS
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SRC
|
set(SRC
|
||||||
|
add_modifier_assets.cc
|
||||||
object_add.cc
|
object_add.cc
|
||||||
object_bake.cc
|
object_bake.cc
|
||||||
object_bake_api.cc
|
object_bake_api.cc
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#include "AS_asset_catalog.hh"
|
||||||
|
#include "AS_asset_catalog_tree.hh"
|
||||||
|
#include "AS_asset_library.hh"
|
||||||
|
#include "AS_asset_representation.hh"
|
||||||
|
|
||||||
|
#include "BLI_multi_value_map.hh"
|
||||||
|
#include "BLI_string.h"
|
||||||
|
|
||||||
|
#include "DNA_modifier_types.h"
|
||||||
|
#include "DNA_screen_types.h"
|
||||||
|
#include "DNA_space_types.h"
|
||||||
|
|
||||||
|
#include "BKE_asset.h"
|
||||||
|
#include "BKE_context.h"
|
||||||
|
#include "BKE_idprop.h"
|
||||||
|
#include "BKE_lib_id.h"
|
||||||
|
#include "BKE_report.h"
|
||||||
|
#include "BKE_screen.h"
|
||||||
|
|
||||||
|
#include "BLT_translation.h"
|
||||||
|
|
||||||
|
#include "RNA_access.hh"
|
||||||
|
|
||||||
|
#include "ED_asset.hh"
|
||||||
|
#include "ED_asset_menu_utils.hh"
|
||||||
|
#include "ED_object.hh"
|
||||||
|
#include "ED_screen.hh"
|
||||||
|
|
||||||
|
#include "MOD_nodes.hh"
|
||||||
|
|
||||||
|
#include "UI_interface.hh"
|
||||||
|
|
||||||
|
#include "WM_api.hh"
|
||||||
|
|
||||||
|
#include "object_intern.h"
|
||||||
|
|
||||||
|
namespace blender::ed::object {
|
||||||
|
|
||||||
|
static bool all_loading_finished()
|
||||||
|
{
|
||||||
|
AssetLibraryReference all_library_ref = asset_system::all_library_reference();
|
||||||
|
return ED_assetlist_is_loaded(&all_library_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static asset::AssetItemTree build_catalog_tree(const bContext &C)
|
||||||
|
{
|
||||||
|
AssetFilterSettings type_filter{};
|
||||||
|
type_filter.id_types = FILTER_ID_NT;
|
||||||
|
auto meta_data_filter = [&](const AssetMetaData &meta_data) {
|
||||||
|
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
|
||||||
|
if (tree_type == nullptr || IDP_Int(tree_type) != NTREE_GEOMETRY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const IDProperty *traits_flag = BKE_asset_metadata_idprop_find(
|
||||||
|
&meta_data, "geometry_node_asset_traits_flag");
|
||||||
|
if (traits_flag == nullptr || !(IDP_Int(traits_flag) & GEO_NODE_ASSET_MODIFIER)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const AssetLibraryReference library = asset_system::all_library_reference();
|
||||||
|
return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static asset::AssetItemTree *get_static_item_tree()
|
||||||
|
{
|
||||||
|
static asset::AssetItemTree tree;
|
||||||
|
return &tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void catalog_assets_draw(const bContext *C, Menu *menu)
|
||||||
|
{
|
||||||
|
bScreen &screen = *CTX_wm_screen(C);
|
||||||
|
asset::AssetItemTree &tree = *get_static_item_tree();
|
||||||
|
|
||||||
|
const PointerRNA menu_path_ptr = CTX_data_pointer_get(C, "asset_catalog_path");
|
||||||
|
if (RNA_pointer_is_null(&menu_path_ptr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const asset_system::AssetCatalogPath &menu_path =
|
||||||
|
*static_cast<const asset_system::AssetCatalogPath *>(menu_path_ptr.data);
|
||||||
|
|
||||||
|
const Span<asset_system::AssetRepresentation *> assets = tree.assets_per_path.lookup(menu_path);
|
||||||
|
asset_system::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(menu_path);
|
||||||
|
BLI_assert(catalog_item != nullptr);
|
||||||
|
|
||||||
|
if (assets.is_empty() && !catalog_item->has_children()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiLayout *layout = menu->layout;
|
||||||
|
uiItemS(layout);
|
||||||
|
|
||||||
|
for (const asset_system::AssetRepresentation *asset : assets) {
|
||||||
|
uiLayout *col = uiLayoutColumn(layout, false);
|
||||||
|
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_asset", true);
|
||||||
|
PointerRNA props_ptr;
|
||||||
|
uiItemFullO_ptr(col,
|
||||||
|
ot,
|
||||||
|
IFACE_(asset->get_name().c_str()),
|
||||||
|
ICON_NONE,
|
||||||
|
nullptr,
|
||||||
|
WM_OP_INVOKE_DEFAULT,
|
||||||
|
UI_ITEM_NONE,
|
||||||
|
&props_ptr);
|
||||||
|
asset::operator_asset_reference_props_set(*asset, props_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
|
||||||
|
asset_system::all_library_reference());
|
||||||
|
if (!all_library) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &item) {
|
||||||
|
asset::draw_menu_for_catalog(
|
||||||
|
screen, *all_library, item, "OBJECT_MT_add_modifier_catalog_assets", *layout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void root_catalogs_draw(const bContext *C, Menu *menu)
|
||||||
|
{
|
||||||
|
const Object *object = ED_object_active_context(C);
|
||||||
|
if (!object) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bScreen &screen = *CTX_wm_screen(C);
|
||||||
|
uiLayout *layout = menu->layout;
|
||||||
|
|
||||||
|
const bool loading_finished = all_loading_finished();
|
||||||
|
|
||||||
|
asset::AssetItemTree &tree = *get_static_item_tree();
|
||||||
|
tree = build_catalog_tree(*C);
|
||||||
|
if (tree.catalogs.is_empty() && loading_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiItemS(layout);
|
||||||
|
|
||||||
|
if (!loading_finished) {
|
||||||
|
uiItemL(layout, IFACE_("Loading Asset Libraries"), ICON_INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Set<std::string> all_builtin_menus = [&]() {
|
||||||
|
Set<std::string> menus;
|
||||||
|
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE)) {
|
||||||
|
menus.add_new("Edit");
|
||||||
|
}
|
||||||
|
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_VOLUME)) {
|
||||||
|
menus.add_new("Generate");
|
||||||
|
}
|
||||||
|
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE, OB_VOLUME)) {
|
||||||
|
menus.add_new("Deform");
|
||||||
|
}
|
||||||
|
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE)) {
|
||||||
|
menus.add_new("Physics");
|
||||||
|
}
|
||||||
|
return menus;
|
||||||
|
}();
|
||||||
|
|
||||||
|
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
|
||||||
|
asset_system::all_library_reference());
|
||||||
|
if (!all_library) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) {
|
||||||
|
if (!all_builtin_menus.contains(item.get_name())) {
|
||||||
|
asset::draw_menu_for_catalog(
|
||||||
|
screen, *all_library, item, "OBJECT_MT_add_modifier_catalog_assets", *layout);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
|
||||||
|
{
|
||||||
|
const asset_system::AssetRepresentation *asset =
|
||||||
|
asset::operator_asset_reference_props_get_asset_from_all_library(C, ptr, reports);
|
||||||
|
if (!asset) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Main &bmain = *CTX_data_main(&C);
|
||||||
|
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
|
||||||
|
asset::asset_local_id_ensure_imported(bmain, *asset));
|
||||||
|
if (!node_group) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (node_group->type != NTREE_GEOMETRY) {
|
||||||
|
if (reports) {
|
||||||
|
BKE_report(reports, RPT_ERROR, "Asset is not a geometry node group");
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return node_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modifier_add_asset_exec(bContext *C, wmOperator *op)
|
||||||
|
{
|
||||||
|
Main *bmain = CTX_data_main(C);
|
||||||
|
Scene *scene = CTX_data_scene(C);
|
||||||
|
Object *object = ED_object_active_context(C);
|
||||||
|
|
||||||
|
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(
|
||||||
|
ED_object_modifier_add(op->reports, bmain, scene, object, nullptr, eModifierType_Nodes));
|
||||||
|
if (!nmd) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bNodeTree *node_group = get_node_group(*C, *op->ptr, op->reports);
|
||||||
|
if (!node_group) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
nmd->node_group = node_group;
|
||||||
|
id_us_plus(&node_group->id);
|
||||||
|
MOD_nodes_update_interface(object, nmd);
|
||||||
|
|
||||||
|
STRNCPY(nmd->modifier.name, DATA_(node_group->id.name + 2));
|
||||||
|
|
||||||
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string modifier_add_asset_get_description(bContext *C,
|
||||||
|
wmOperatorType * /*ot*/,
|
||||||
|
PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
const asset_system::AssetRepresentation *asset =
|
||||||
|
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *ptr, nullptr);
|
||||||
|
if (!asset) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (!asset->get_metadata().description) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return TIP_(asset->get_metadata().description);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OBJECT_OT_modifier_add_asset(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
ot->name = "Add Modifier";
|
||||||
|
ot->description = "Add a procedural operation/effect to the active object";
|
||||||
|
ot->idname = "OBJECT_OT_modifier_add_asset";
|
||||||
|
|
||||||
|
ot->exec = modifier_add_asset_exec;
|
||||||
|
ot->poll = ED_operator_object_active_editable;
|
||||||
|
ot->get_description = modifier_add_asset_get_description;
|
||||||
|
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
asset::operator_asset_reference_props_register(*ot->srna);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MenuType modifier_add_catalog_assets_menu_type()
|
||||||
|
{
|
||||||
|
MenuType type{};
|
||||||
|
STRNCPY(type.idname, "OBJECT_MT_add_modifier_catalog_assets");
|
||||||
|
type.draw = catalog_assets_draw;
|
||||||
|
type.listener = asset::asset_reading_region_listen_fn;
|
||||||
|
type.context_dependent = true;
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MenuType modifier_add_root_catalogs_menu_type()
|
||||||
|
{
|
||||||
|
MenuType type{};
|
||||||
|
STRNCPY(type.idname, "OBJECT_MT_modifier_add_root_catalogs");
|
||||||
|
type.draw = root_catalogs_draw;
|
||||||
|
type.listener = asset::asset_reading_region_listen_fn;
|
||||||
|
type.context_dependent = true;
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void object_modifier_add_asset_register()
|
||||||
|
{
|
||||||
|
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_catalog_assets_menu_type()));
|
||||||
|
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_root_catalogs_menu_type()));
|
||||||
|
WM_operatortype_append(OBJECT_OT_modifier_add_asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_template_modifier_asset_menu_items(uiLayout &layout,
|
||||||
|
bContext &C,
|
||||||
|
const StringRef catalog_path)
|
||||||
|
{
|
||||||
|
using namespace blender;
|
||||||
|
using namespace blender::ed;
|
||||||
|
using namespace blender::ed::object;
|
||||||
|
bScreen &screen = *CTX_wm_screen(&C);
|
||||||
|
asset::AssetItemTree &tree = *get_static_item_tree();
|
||||||
|
const asset_system::AssetCatalogTreeItem *item = tree.catalogs.find_root_item(catalog_path);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
|
||||||
|
asset_system::all_library_reference());
|
||||||
|
if (!all_library) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(screen, *all_library, *item);
|
||||||
|
if (path_ptr.data == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uiItemS(&layout);
|
||||||
|
uiLayout *col = uiLayoutColumn(&layout, false);
|
||||||
|
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
||||||
|
uiItemMContents(col, "OBJECT_MT_add_modifier_catalog_assets");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blender::ed::object
|
|
@ -370,3 +370,13 @@ void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
namespace blender::ed::object {
|
||||||
|
|
||||||
|
void object_modifier_add_asset_register();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
void ED_operatortypes_object()
|
void ED_operatortypes_object()
|
||||||
{
|
{
|
||||||
|
using namespace blender::ed::object;
|
||||||
WM_operatortype_append(OBJECT_OT_location_clear);
|
WM_operatortype_append(OBJECT_OT_location_clear);
|
||||||
WM_operatortype_append(OBJECT_OT_rotation_clear);
|
WM_operatortype_append(OBJECT_OT_rotation_clear);
|
||||||
WM_operatortype_append(OBJECT_OT_scale_clear);
|
WM_operatortype_append(OBJECT_OT_scale_clear);
|
||||||
|
@ -289,6 +290,8 @@ void ED_operatortypes_object()
|
||||||
WM_operatortype_append(OBJECT_OT_light_linking_blockers_link);
|
WM_operatortype_append(OBJECT_OT_light_linking_blockers_link);
|
||||||
|
|
||||||
WM_operatortype_append(OBJECT_OT_light_linking_unlink_from_collection);
|
WM_operatortype_append(OBJECT_OT_light_linking_unlink_from_collection);
|
||||||
|
|
||||||
|
object_modifier_add_asset_register();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ED_operatormacros_object()
|
void ED_operatormacros_object()
|
||||||
|
|
|
@ -1022,6 +1022,28 @@ static blender::float2 calculate_pixels_per_unit(View2D *v2d)
|
||||||
return pixels_per_unit;
|
return pixels_per_unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float calculate_pixel_distance(const rctf &bounds, const blender::float2 pixels_per_unit)
|
||||||
|
{
|
||||||
|
return BLI_rctf_size_x(&bounds) * pixels_per_unit[0] +
|
||||||
|
BLI_rctf_size_y(&bounds) * pixels_per_unit[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expand_key_bounds(const BezTriple *left_key, const BezTriple *right_key, rctf &bounds)
|
||||||
|
{
|
||||||
|
bounds.xmax = right_key->vec[1][0];
|
||||||
|
if (left_key->ipo == BEZT_IPO_BEZ) {
|
||||||
|
/* Respect handles of bezier keys. */
|
||||||
|
bounds.ymin = min_ffff(
|
||||||
|
bounds.ymin, right_key->vec[1][1], right_key->vec[0][1], left_key->vec[2][1]);
|
||||||
|
bounds.ymax = max_ffff(
|
||||||
|
bounds.ymax, right_key->vec[1][1], right_key->vec[0][1], left_key->vec[2][1]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bounds.ymax = max_ff(bounds.ymax, right_key->vec[1][1]);
|
||||||
|
bounds.ymin = min_ff(bounds.ymin, right_key->vec[1][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Helper function - draw one repeat of an F-Curve (using Bezier curve approximations). */
|
/* Helper function - draw one repeat of an F-Curve (using Bezier curve approximations). */
|
||||||
static void draw_fcurve_curve_keys(
|
static void draw_fcurve_curve_keys(
|
||||||
bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, uint pos, const bool draw_extrapolation)
|
bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, uint pos, const bool draw_extrapolation)
|
||||||
|
@ -1062,10 +1084,39 @@ static void draw_fcurve_curve_keys(
|
||||||
const float samples_per_pixel = 0.66f;
|
const float samples_per_pixel = 0.66f;
|
||||||
const float evaluation_step = pixel_width / samples_per_pixel;
|
const float evaluation_step = pixel_width / samples_per_pixel;
|
||||||
|
|
||||||
|
BezTriple *first_key = &fcu->bezt[bounding_indices[0]];
|
||||||
|
rctf key_bounds = {
|
||||||
|
first_key->vec[1][0], first_key->vec[1][1], first_key->vec[1][0], first_key->vec[1][1]};
|
||||||
|
/* Used when skipping keys. */
|
||||||
|
bool has_skipped_keys = false;
|
||||||
|
const float min_pixel_distance = 3.0f;
|
||||||
|
|
||||||
/* Draw curve between first and last keyframe (if there are enough to do so). */
|
/* Draw curve between first and last keyframe (if there are enough to do so). */
|
||||||
for (int i = bounding_indices[0] + 1; i <= bounding_indices[1]; i++) {
|
for (int i = bounding_indices[0] + 1; i <= bounding_indices[1]; i++) {
|
||||||
BezTriple *prevbezt = &fcu->bezt[i - 1];
|
BezTriple *prevbezt = &fcu->bezt[i - 1];
|
||||||
BezTriple *bezt = &fcu->bezt[i];
|
BezTriple *bezt = &fcu->bezt[i];
|
||||||
|
expand_key_bounds(prevbezt, bezt, key_bounds);
|
||||||
|
float pixel_distance = calculate_pixel_distance(key_bounds, pixels_per_unit);
|
||||||
|
|
||||||
|
if (pixel_distance >= min_pixel_distance && has_skipped_keys) {
|
||||||
|
/* When the pixel distance is greater than the threshold, and we've skipped at least one, add
|
||||||
|
* a point. The point position is the average of all keys from INCLUDING prevbezt to
|
||||||
|
* EXCLUDING bezt. prevbezt then gets reset to the key before bezt because the distance
|
||||||
|
* between those is potentially below the threshold. */
|
||||||
|
curve_vertices.append({BLI_rctf_cent_x(&key_bounds), BLI_rctf_cent_y(&key_bounds)});
|
||||||
|
has_skipped_keys = false;
|
||||||
|
key_bounds = {
|
||||||
|
prevbezt->vec[1][0], prevbezt->vec[1][1], prevbezt->vec[1][0], prevbezt->vec[1][1]};
|
||||||
|
expand_key_bounds(prevbezt, bezt, key_bounds);
|
||||||
|
/* Calculate again based on the new prevbezt. */
|
||||||
|
pixel_distance = calculate_pixel_distance(key_bounds, pixels_per_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pixel_distance < min_pixel_distance) {
|
||||||
|
/* Skip any keys that are too close to each other in screen space. */
|
||||||
|
has_skipped_keys = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (prevbezt->ipo) {
|
switch (prevbezt->ipo) {
|
||||||
|
|
||||||
|
@ -1099,12 +1150,13 @@ static void draw_fcurve_curve_keys(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Last point? */
|
prevbezt = bezt;
|
||||||
if (i == bounding_indices[1]) {
|
|
||||||
curve_vertices.append({bezt->vec[1][0], bezt->vec[1][1]});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Always add the last point so the extrapolation line doesn't jump. */
|
||||||
|
curve_vertices.append(
|
||||||
|
{fcu->bezt[bounding_indices[1]].vec[1][0], fcu->bezt[bounding_indices[1]].vec[1][1]});
|
||||||
|
|
||||||
/* Extrapolate to the right? (see code for left-extrapolation above too) */
|
/* Extrapolate to the right? (see code for left-extrapolation above too) */
|
||||||
if (draw_extrapolation && fcu->bezt[fcu->totvert - 1].vec[1][0] < v2d->cur.xmax) {
|
if (draw_extrapolation && fcu->bezt[fcu->totvert - 1].vec[1][0] < v2d->cur.xmax) {
|
||||||
add_extrapolation_point_right(fcu, v2d->cur.xmax, curve_vertices);
|
add_extrapolation_point_right(fcu, v2d->cur.xmax, curve_vertices);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "RNA_access.hh"
|
#include "RNA_access.hh"
|
||||||
|
|
||||||
#include "ED_asset.hh"
|
#include "ED_asset.hh"
|
||||||
|
#include "ED_asset_menu_utils.hh"
|
||||||
#include "ED_screen.hh"
|
#include "ED_screen.hh"
|
||||||
|
|
||||||
#include "node_intern.hh"
|
#include "node_intern.hh"
|
||||||
|
@ -99,17 +100,9 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &child_item) {
|
catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &item) {
|
||||||
PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(
|
asset::draw_menu_for_catalog(
|
||||||
screen, *all_library, child_item);
|
screen, *all_library, item, "NODE_MT_node_add_catalog_assets", *layout);
|
||||||
if (path_ptr.data == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uiLayout *col = uiLayoutColumn(layout, false);
|
|
||||||
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
|
||||||
uiItemM(
|
|
||||||
col, "NODE_MT_node_add_catalog_assets", IFACE_(child_item.get_name().c_str()), ICON_NONE);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,16 +174,10 @@ static void add_root_catalogs_draw(const bContext *C, Menu *menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
tree.catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) {
|
tree.catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) {
|
||||||
if (all_builtin_menus.contains(item.get_name())) {
|
if (!all_builtin_menus.contains(item.get_name())) {
|
||||||
return;
|
asset::draw_menu_for_catalog(
|
||||||
|
screen, *all_library, item, "NODE_MT_node_add_catalog_assets", *layout);
|
||||||
}
|
}
|
||||||
PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(screen, *all_library, item);
|
|
||||||
if (path_ptr.data == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uiLayout *col = uiLayoutColumn(layout, false);
|
|
||||||
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
|
||||||
uiItemM(col, "NODE_MT_node_add_catalog_assets", IFACE_(item.get_name().c_str()), ICON_NONE);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -488,7 +488,7 @@ static void createTransNlaData(bContext *C, TransInfo *t)
|
||||||
if (strip->type == NLASTRIP_TYPE_TRANSITION) {
|
if (strip->type == NLASTRIP_TYPE_TRANSITION) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strip->flag & NLASTRIP_FLAG_SELECT == 0) {
|
if ((strip->flag & NLASTRIP_FLAG_SELECT) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (FrameOnMouseSide(t->frame_side, strip->start, float(scene->r.cfra))) {
|
if (FrameOnMouseSide(t->frame_side, strip->start, float(scene->r.cfra))) {
|
||||||
|
@ -539,7 +539,7 @@ static void createTransNlaData(bContext *C, TransInfo *t)
|
||||||
if (strip->type == NLASTRIP_TYPE_TRANSITION) {
|
if (strip->type == NLASTRIP_TYPE_TRANSITION) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strip->flag & NLASTRIP_FLAG_SELECT == 0) {
|
if ((strip->flag & NLASTRIP_FLAG_SELECT) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,7 +654,7 @@ static void snap_transform_data(TransInfo *t, TransDataContainer *tc)
|
||||||
if (t->state == TRANS_CANCEL) {
|
if (t->state == TRANS_CANCEL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (t->tsnap.flag & SCE_SNAP == 0) {
|
if ((t->tsnap.flag & SCE_SNAP) == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ set(SRC
|
||||||
../include/ED_anim_api.hh
|
../include/ED_anim_api.hh
|
||||||
../include/ED_armature.hh
|
../include/ED_armature.hh
|
||||||
../include/ED_asset.hh
|
../include/ED_asset.hh
|
||||||
|
../include/ED_asset_menu_utils.hh
|
||||||
../include/ED_buttons.hh
|
../include/ED_buttons.hh
|
||||||
../include/ED_clip.hh
|
../include/ED_clip.hh
|
||||||
../include/ED_curve.hh
|
../include/ED_curve.hh
|
||||||
|
|
|
@ -129,6 +129,11 @@ void GLVaoCache::remove(const GLShaderInterface *interface)
|
||||||
break; /* cannot have duplicates */
|
break; /* cannot have duplicates */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (interface_ == interface) {
|
||||||
|
interface_ = nullptr;
|
||||||
|
vao_id_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLVaoCache::clear()
|
void GLVaoCache::clear()
|
||||||
|
|
|
@ -897,6 +897,7 @@ typedef enum GeometryNodeAssetTraitFlag {
|
||||||
GEO_NODE_ASSET_MESH = (1 << 3),
|
GEO_NODE_ASSET_MESH = (1 << 3),
|
||||||
GEO_NODE_ASSET_CURVE = (1 << 4),
|
GEO_NODE_ASSET_CURVE = (1 << 4),
|
||||||
GEO_NODE_ASSET_POINT_CLOUD = (1 << 5),
|
GEO_NODE_ASSET_POINT_CLOUD = (1 << 5),
|
||||||
|
GEO_NODE_ASSET_MODIFIER = (1 << 6),
|
||||||
} GeometryNodeAssetTraitFlag;
|
} GeometryNodeAssetTraitFlag;
|
||||||
ENUM_OPERATORS(GeometryNodeAssetTraitFlag, GEO_NODE_ASSET_POINT_CLOUD);
|
ENUM_OPERATORS(GeometryNodeAssetTraitFlag, GEO_NODE_ASSET_POINT_CLOUD);
|
||||||
|
|
||||||
|
|
|
@ -1785,6 +1785,15 @@ static void rna_GeometryNodeTree_is_tool_set(PointerRNA *ptr, bool value)
|
||||||
geometry_node_asset_trait_flag_set(ptr, GEO_NODE_ASSET_TOOL, value);
|
geometry_node_asset_trait_flag_set(ptr, GEO_NODE_ASSET_TOOL, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rna_GeometryNodeTree_is_modifier_get(PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
return geometry_node_asset_trait_flag_get(ptr, GEO_NODE_ASSET_MODIFIER);
|
||||||
|
}
|
||||||
|
static void rna_GeometryNodeTree_is_modifier_set(PointerRNA *ptr, bool value)
|
||||||
|
{
|
||||||
|
geometry_node_asset_trait_flag_set(ptr, GEO_NODE_ASSET_MODIFIER, value);
|
||||||
|
}
|
||||||
|
|
||||||
static bool rna_GeometryNodeTree_is_mode_edit_get(PointerRNA *ptr)
|
static bool rna_GeometryNodeTree_is_mode_edit_get(PointerRNA *ptr)
|
||||||
{
|
{
|
||||||
return geometry_node_asset_trait_flag_get(ptr, GEO_NODE_ASSET_EDIT);
|
return geometry_node_asset_trait_flag_get(ptr, GEO_NODE_ASSET_EDIT);
|
||||||
|
@ -10308,6 +10317,14 @@ static void rna_def_geometry_nodetree(BlenderRNA *brna)
|
||||||
prop, "rna_GeometryNodeTree_is_tool_get", "rna_GeometryNodeTree_is_tool_set");
|
prop, "rna_GeometryNodeTree_is_tool_get", "rna_GeometryNodeTree_is_tool_set");
|
||||||
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update");
|
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "is_modifier", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GEO_NODE_ASSET_MODIFIER);
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Modifier", "The node group is used as a geometry modifier");
|
||||||
|
RNA_def_property_boolean_funcs(
|
||||||
|
prop, "rna_GeometryNodeTree_is_modifier_get", "rna_GeometryNodeTree_is_modifier_set");
|
||||||
|
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update");
|
||||||
|
|
||||||
prop = RNA_def_property(srna, "is_mode_edit", PROP_BOOLEAN, PROP_NONE);
|
prop = RNA_def_property(srna, "is_mode_edit", PROP_BOOLEAN, PROP_NONE);
|
||||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GEO_NODE_ASSET_EDIT);
|
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GEO_NODE_ASSET_EDIT);
|
||||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||||
|
|
|
@ -38,6 +38,7 @@ const EnumPropertyItem rna_enum_icon_items[] = {
|
||||||
# include "DNA_asset_types.h"
|
# include "DNA_asset_types.h"
|
||||||
|
|
||||||
# include "ED_geometry.hh"
|
# include "ED_geometry.hh"
|
||||||
|
# include "ED_object.hh"
|
||||||
|
|
||||||
const char *rna_translate_ui_text(
|
const char *rna_translate_ui_text(
|
||||||
const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, bool translate)
|
const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, bool translate)
|
||||||
|
@ -786,6 +787,16 @@ static void rna_uiLayout_template_node_operator_asset_menu_items(uiLayout *layou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_uiLayout_template_modifier_asset_menu_items(uiLayout *layout,
|
||||||
|
bContext *C,
|
||||||
|
const char *catalog_path)
|
||||||
|
{
|
||||||
|
if (U.experimental.use_node_group_operators) {
|
||||||
|
blender::ed::object::ui_template_modifier_asset_menu_items(
|
||||||
|
*layout, *C, blender::StringRef(catalog_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void rna_uiLayout_template_node_operator_root_items(uiLayout *layout, bContext *C)
|
static void rna_uiLayout_template_node_operator_root_items(uiLayout *layout, bContext *C)
|
||||||
{
|
{
|
||||||
if (U.experimental.use_node_group_operators) {
|
if (U.experimental.use_node_group_operators) {
|
||||||
|
@ -1893,6 +1904,12 @@ void RNA_api_ui_layout(StructRNA *srna)
|
||||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||||
parm = RNA_def_string(func, "catalog_path", nullptr, 0, "", "");
|
parm = RNA_def_string(func, "catalog_path", nullptr, 0, "", "");
|
||||||
|
|
||||||
|
func = RNA_def_function(srna,
|
||||||
|
"template_modifier_asset_menu_items",
|
||||||
|
"rna_uiLayout_template_modifier_asset_menu_items");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||||
|
parm = RNA_def_string(func, "catalog_path", nullptr, 0, "", "");
|
||||||
|
|
||||||
func = RNA_def_function(srna,
|
func = RNA_def_function(srna,
|
||||||
"template_node_operator_asset_menu_items",
|
"template_node_operator_asset_menu_items",
|
||||||
"rna_uiLayout_template_node_operator_asset_menu_items");
|
"rna_uiLayout_template_node_operator_asset_menu_items");
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#include "BKE_scene.h"
|
#include "BKE_scene.h"
|
||||||
#include "BKE_sound.h"
|
#include "BKE_sound.h"
|
||||||
|
|
||||||
#ifdef WITH_AUDASPACE
|
#ifdef WITH_CONVOLUTION
|
||||||
# include "AUD_Sound.h"
|
# include "AUD_Sound.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
const SoundModifierWorkerInfo workersSoundModifiers[] = {
|
const SoundModifierWorkerInfo workersSoundModifiers[] = {
|
||||||
{seqModifierType_SoundEqualizer, SEQ_sound_equalizermodifier_recreator}, {0, nullptr}};
|
{seqModifierType_SoundEqualizer, SEQ_sound_equalizermodifier_recreator}, {0, nullptr}};
|
||||||
|
|
||||||
#ifdef WITH_AUDASPACE
|
#ifdef WITH_CONVOLUTION
|
||||||
static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, ListBase *seqbase)
|
static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, ListBase *seqbase)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
@ -79,7 +79,7 @@ static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene,
|
||||||
|
|
||||||
void SEQ_sound_update_length(Main *bmain, Scene *scene)
|
void SEQ_sound_update_length(Main *bmain, Scene *scene)
|
||||||
{
|
{
|
||||||
#ifdef WITH_AUDASPACE
|
#ifdef WITH_CONVOLUTION
|
||||||
if (scene->ed) {
|
if (scene->ed) {
|
||||||
sequencer_refresh_sound_length_recursive(bmain, scene, &scene->ed->seqbase);
|
sequencer_refresh_sound_length_recursive(bmain, scene, &scene->ed->seqbase);
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,7 @@ void SEQ_sound_equalizermodifier_copy_data(SequenceModifierData *target, Sequenc
|
||||||
|
|
||||||
void *SEQ_sound_equalizermodifier_recreator(Sequence *seq, SequenceModifierData *smd, void *sound)
|
void *SEQ_sound_equalizermodifier_recreator(Sequence *seq, SequenceModifierData *smd, void *sound)
|
||||||
{
|
{
|
||||||
#ifdef WITH_AUDASPACE
|
#ifdef WITH_CONVOLUTION
|
||||||
UNUSED_VARS(seq);
|
UNUSED_VARS(seq);
|
||||||
|
|
||||||
SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd;
|
SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd;
|
||||||
|
|
|
@ -314,7 +314,14 @@ add_blender_test(
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# NODE INTERFACE TESTS
|
# NODE GROUP TESTS
|
||||||
|
add_blender_test(
|
||||||
|
bl_node_group_compat
|
||||||
|
--python ${CMAKE_CURRENT_LIST_DIR}/bl_node_group_compat.py
|
||||||
|
--
|
||||||
|
--testdir "${TEST_SRC_DIR}/node_group"
|
||||||
|
)
|
||||||
|
|
||||||
add_blender_test(
|
add_blender_test(
|
||||||
bl_node_group_interface
|
bl_node_group_interface
|
||||||
--python ${CMAKE_CURRENT_LIST_DIR}/bl_node_group_interface.py
|
--python ${CMAKE_CURRENT_LIST_DIR}/bl_node_group_interface.py
|
||||||
|
|
|
@ -0,0 +1,347 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021-2023 Blender Foundation
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import tempfile
|
||||||
|
import math
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
args = None
|
||||||
|
|
||||||
|
|
||||||
|
type_info = {
|
||||||
|
("VALUE", "NONE"): "NodeSocketFloat",
|
||||||
|
("VALUE", "UNSIGNED"): "NodeSocketFloatUnsigned",
|
||||||
|
("VALUE", "PERCENTAGE"): "NodeSocketFloatPercentage",
|
||||||
|
("VALUE", "FACTOR"): "NodeSocketFloatFactor",
|
||||||
|
("VALUE", "ANGLE"): "NodeSocketFloatAngle",
|
||||||
|
("VALUE", "TIME"): "NodeSocketFloatTime",
|
||||||
|
("VALUE", "TIME_ABSOLUTE"): "NodeSocketFloatTimeAbsolute",
|
||||||
|
("VALUE", "DISTANCE"): "NodeSocketFloatDistance",
|
||||||
|
("INT", "NONE"): "NodeSocketInt",
|
||||||
|
("INT", "UNSIGNED"): "NodeSocketIntUnsigned",
|
||||||
|
("INT", "PERCENTAGE"): "NodeSocketIntPercentage",
|
||||||
|
("INT", "FACTOR"): "NodeSocketIntFactor",
|
||||||
|
("BOOLEAN", "NONE"): "NodeSocketBool",
|
||||||
|
("ROTATION", "NONE"): "NodeSocketRotation",
|
||||||
|
("VECTOR", "NONE"): "NodeSocketVector",
|
||||||
|
("VECTOR", "TRANSLATION"): "NodeSocketVectorTranslation",
|
||||||
|
("VECTOR", "DIRECTION"): "NodeSocketVectorDirection",
|
||||||
|
("VECTOR", "VELOCITY"): "NodeSocketVectorVelocity",
|
||||||
|
("VECTOR", "ACCELERATION"): "NodeSocketVectorAcceleration",
|
||||||
|
("VECTOR", "EULER"): "NodeSocketVectorEuler",
|
||||||
|
("VECTOR", "XYZ"): "NodeSocketVectorXYZ",
|
||||||
|
("RGBA", "NONE"): "NodeSocketColor",
|
||||||
|
("STRING", "NONE"): "NodeSocketString",
|
||||||
|
("SHADER", "NONE"): "NodeSocketShader",
|
||||||
|
("OBJECT", "NONE"): "NodeSocketObject",
|
||||||
|
("IMAGE", "NONE"): "NodeSocketImage",
|
||||||
|
("GEOMETRY", "NONE"): "NodeSocketGeometry",
|
||||||
|
("COLLECTION", "NONE"): "NodeSocketCollection",
|
||||||
|
("TEXTURE", "NONE"): "NodeSocketTexture",
|
||||||
|
("MATERIAL", "NONE"): "NodeSocketMaterial",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SocketSpec():
|
||||||
|
name: str
|
||||||
|
identifier: str
|
||||||
|
type: str
|
||||||
|
subtype: str = 'NONE'
|
||||||
|
hide_value: bool = False
|
||||||
|
hide_in_modifier: bool = False
|
||||||
|
default_value: object = None
|
||||||
|
min_value: object = None
|
||||||
|
max_value: object = None
|
||||||
|
internal_links: int = 1
|
||||||
|
external_links: int = 1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def idname(self):
|
||||||
|
return type_info[(self.type, self.subtype)]
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractNodeGroupInterfaceTest(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.testdir = args.testdir
|
||||||
|
cls._tempdir = tempfile.TemporaryDirectory()
|
||||||
|
cls.tempdir = pathlib.Path(cls._tempdir.name)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.assertTrue(self.testdir.exists(),
|
||||||
|
'Test dir {0} should exist'.format(self.testdir))
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self._tempdir.cleanup()
|
||||||
|
|
||||||
|
def subtype_compare(self, value, expected, subtype):
|
||||||
|
# Rounding errors introduced at various levels, only check for roughly expected values.
|
||||||
|
if subtype in {'ANGLE', 'EULER'}:
|
||||||
|
# Angle values are shown in degrees in the UI, but stored as radians.
|
||||||
|
self.assertAlmostEqual(value, math.radians(expected))
|
||||||
|
else:
|
||||||
|
self.assertAlmostEqual(value, expected)
|
||||||
|
|
||||||
|
# Test properties of a node group item and associated node socket with spec data.
|
||||||
|
def compare_group_socket_to_spec(self, item, node, spec: SocketSpec, test_links=True):
|
||||||
|
group = item.id_data
|
||||||
|
|
||||||
|
# Examine the interface item.
|
||||||
|
self.assertEqual(item.name, spec.name)
|
||||||
|
self.assertEqual(item.bl_socket_idname, spec.idname)
|
||||||
|
self.assertEqual(item.identifier, spec.identifier)
|
||||||
|
|
||||||
|
# Types that have subtypes.
|
||||||
|
if spec.type in {'VALUE', 'INT', 'VECTOR'}:
|
||||||
|
self.assertEqual(item.subtype, spec.subtype)
|
||||||
|
|
||||||
|
self.assertEqual(item.hide_value, spec.hide_value)
|
||||||
|
self.assertEqual(item.hide_in_modifier, spec.hide_in_modifier)
|
||||||
|
|
||||||
|
if spec.type in {'VALUE', 'INT'}:
|
||||||
|
self.subtype_compare(item.default_value, spec.default_value, spec.subtype)
|
||||||
|
self.assertEqual(item.min_value, spec.min_value)
|
||||||
|
self.assertEqual(item.max_value, spec.max_value)
|
||||||
|
elif spec.type == 'VECTOR':
|
||||||
|
self.subtype_compare(item.default_value[0], spec.default_value[0], spec.subtype)
|
||||||
|
self.subtype_compare(item.default_value[1], spec.default_value[1], spec.subtype)
|
||||||
|
self.subtype_compare(item.default_value[2], spec.default_value[2], spec.subtype)
|
||||||
|
self.assertEqual(item.min_value, spec.min_value)
|
||||||
|
self.assertEqual(item.max_value, spec.max_value)
|
||||||
|
elif spec.type == 'RGBA':
|
||||||
|
# Colors stored as int8 internally, enough rounding error to require fuzzy test.
|
||||||
|
self.assertAlmostEqual(item.default_value[0], spec.default_value[0])
|
||||||
|
self.assertAlmostEqual(item.default_value[1], spec.default_value[1])
|
||||||
|
self.assertAlmostEqual(item.default_value[2], spec.default_value[2])
|
||||||
|
self.assertAlmostEqual(item.default_value[3], spec.default_value[3])
|
||||||
|
elif spec.type in {'STRING', 'BOOLEAN', 'MATERIAL', 'TEXTURE', 'OBJECT', 'COLLECTION', 'IMAGE'}:
|
||||||
|
self.assertEqual(item.default_value, spec.default_value)
|
||||||
|
elif spec.type in {'SHADER', 'GEOMETRY'}:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Add socket type testing above if this happens.
|
||||||
|
self.fail("Socket type not supported by test")
|
||||||
|
|
||||||
|
# Examine the node socket.
|
||||||
|
if 'INPUT' in item.in_out:
|
||||||
|
socket = next(s for s in node.inputs if s.identifier == spec.identifier)
|
||||||
|
self.assertIsNotNone(socket, f"Could not find socket for group input identifier {spec.identifier}")
|
||||||
|
self.assertEqual(socket.name, spec.name)
|
||||||
|
self.assertEqual(socket.bl_idname, spec.idname)
|
||||||
|
self.assertEqual(socket.type, spec.type)
|
||||||
|
self.assertEqual(socket.hide_value, spec.hide_value)
|
||||||
|
if test_links:
|
||||||
|
self.assertEqual(len(socket.links), spec.external_links,
|
||||||
|
f"Socket should have exactly {spec.external_links} external connections")
|
||||||
|
|
||||||
|
input_node = next(n for n in group.nodes if n.bl_idname == 'NodeGroupInput')
|
||||||
|
self.assertIsNotNone(input_node, "Could not find an input node in the group")
|
||||||
|
socket = next(s for s in input_node.outputs if s.identifier == spec.identifier)
|
||||||
|
self.assertIsNotNone(
|
||||||
|
socket, f"Could not find group input socket for group input identifier {spec.identifier}")
|
||||||
|
self.assertEqual(socket.name, spec.name)
|
||||||
|
self.assertEqual(socket.bl_idname, spec.idname)
|
||||||
|
self.assertEqual(socket.type, spec.type)
|
||||||
|
self.assertEqual(socket.hide_value, spec.hide_value)
|
||||||
|
if test_links:
|
||||||
|
self.assertEqual(len(socket.links), spec.internal_links,
|
||||||
|
f"Socket should have exactly {spec.internal_links} internal connections")
|
||||||
|
|
||||||
|
if 'OUTPUT' in item.in_out:
|
||||||
|
socket = next(s for s in node.outputs if s.identifier == spec.identifier)
|
||||||
|
self.assertIsNotNone(socket, f"Could not find socket for group output identifier {spec.identifier}")
|
||||||
|
self.assertEqual(socket.name, spec.name)
|
||||||
|
self.assertEqual(socket.bl_idname, spec.idname)
|
||||||
|
self.assertEqual(socket.type, spec.type)
|
||||||
|
self.assertEqual(socket.hide_value, spec.hide_value)
|
||||||
|
if test_links:
|
||||||
|
self.assertEqual(len(socket.links), spec.external_links,
|
||||||
|
f"Socket should have exactly {spec.external_links} external connections")
|
||||||
|
|
||||||
|
output_node = next(n for n in group.nodes if n.bl_idname == 'NodeGroupOutput')
|
||||||
|
self.assertIsNotNone(output_node, "Could not find an output node in the group")
|
||||||
|
socket = next(s for s in output_node.inputs if s.identifier == spec.identifier)
|
||||||
|
self.assertIsNotNone(
|
||||||
|
socket, f"Could not find group output socket for group output identifier {spec.identifier}")
|
||||||
|
self.assertEqual(socket.name, spec.name)
|
||||||
|
self.assertEqual(socket.bl_idname, spec.idname)
|
||||||
|
self.assertEqual(socket.type, spec.type)
|
||||||
|
self.assertEqual(socket.hide_value, spec.hide_value)
|
||||||
|
if test_links:
|
||||||
|
self.assertEqual(len(socket.links), spec.internal_links,
|
||||||
|
f"Socket should have exactly {spec.internal_links} internal connections")
|
||||||
|
|
||||||
|
# Test node group items and associated node sockets with spec data.
|
||||||
|
def compare_group_to_specs(self, group, node, specs, test_links=True):
|
||||||
|
for index, spec in enumerate(specs):
|
||||||
|
self.compare_group_socket_to_spec(group.interface.ui_items[index], node, spec, test_links=test_links)
|
||||||
|
|
||||||
|
class NodeGroupVersioning36Test(AbstractNodeGroupInterfaceTest):
|
||||||
|
def open_file(self):
|
||||||
|
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "nodegroup36.blend"))
|
||||||
|
self.assertEqual(bpy.data.version, (3, 6, 11))
|
||||||
|
|
||||||
|
def test_load_compositor_nodes(self):
|
||||||
|
self.open_file()
|
||||||
|
|
||||||
|
tree = bpy.data.scenes['Scene'].node_tree
|
||||||
|
group = bpy.data.node_groups.get('NodeGroup')
|
||||||
|
self.assertIsNotNone(group, "Compositor node group not found")
|
||||||
|
node = tree.nodes['Group']
|
||||||
|
self.assertEqual(node.node_tree, group, "Node group must use compositor node tree")
|
||||||
|
|
||||||
|
# autopep8: off
|
||||||
|
self.compare_group_to_specs(group, node, [
|
||||||
|
SocketSpec("Output Float", "Output_9", "VALUE", hide_value=True, default_value=3.0, min_value=1.0, max_value=1.0),
|
||||||
|
SocketSpec("Output Vector", "Output_10", "VECTOR", subtype="EULER", default_value=( 10, 20, 30), min_value=-10.0, max_value=10.0),
|
||||||
|
SocketSpec("Output Color", "Output_11", "RGBA", default_value=(0, 1, 1, 1)),
|
||||||
|
|
||||||
|
SocketSpec("Input Float", "Input_6", "VALUE", subtype="ANGLE", default_value=-20.0, min_value=5.0, max_value=6.0),
|
||||||
|
SocketSpec("Input Vector", "Input_7", "VECTOR", hide_value=True, default_value=( 2, 4, 6), min_value=-4.0, max_value=100.0),
|
||||||
|
SocketSpec("Input Color", "Input_8", "RGBA", default_value=(0.5, 0.4, 0.3, 0.2)),
|
||||||
|
])
|
||||||
|
# autopep8: on
|
||||||
|
|
||||||
|
def test_load_shader_nodes(self):
|
||||||
|
self.open_file()
|
||||||
|
|
||||||
|
tree = bpy.data.materials['Material'].node_tree
|
||||||
|
group = bpy.data.node_groups.get('NodeGroup.003')
|
||||||
|
self.assertIsNotNone(group, "Shader node group not found")
|
||||||
|
node = tree.nodes['Group']
|
||||||
|
self.assertEqual(node.node_tree, group, "Node group must use shader node tree")
|
||||||
|
|
||||||
|
# autopep8: off
|
||||||
|
self.compare_group_to_specs(group, node, [
|
||||||
|
SocketSpec("Output Float", "Output_30", "VALUE", hide_value=True, default_value=3.0, min_value=1.0, max_value=1.0),
|
||||||
|
SocketSpec("Output Vector", "Output_31", "VECTOR", subtype="EULER", default_value=( 10, 20, 30), min_value=-10.0, max_value=10.0),
|
||||||
|
SocketSpec("Output Color", "Output_32", "RGBA", default_value=(0, 1, 1, 1)),
|
||||||
|
SocketSpec("Output Shader", "Output_33", "SHADER"),
|
||||||
|
|
||||||
|
SocketSpec("Input Float", "Input_26", "VALUE", subtype="ANGLE", default_value=-20.0, min_value=5.0, max_value=6.0),
|
||||||
|
SocketSpec("Input Vector", "Input_27", "VECTOR", hide_value=True, default_value=( 2, 4, 6), min_value=-4.0, max_value=100.0),
|
||||||
|
SocketSpec("Input Color", "Input_28", "RGBA", default_value=(0.5, 0.4, 0.3, 0.2)),
|
||||||
|
SocketSpec("Input Shader", "Input_29", "SHADER"),
|
||||||
|
])
|
||||||
|
# autopep8: on
|
||||||
|
|
||||||
|
def test_load_geometry_nodes(self):
|
||||||
|
self.open_file()
|
||||||
|
|
||||||
|
tree = bpy.data.node_groups['Geometry Nodes']
|
||||||
|
group = bpy.data.node_groups.get('NodeGroup.002')
|
||||||
|
self.assertIsNotNone(group, "Geometry node group not found")
|
||||||
|
node = tree.nodes['Group']
|
||||||
|
self.assertEqual(node.node_tree, group, "Node group must use geometry node tree")
|
||||||
|
|
||||||
|
# autopep8: off
|
||||||
|
self.compare_group_to_specs(group, node, [
|
||||||
|
SocketSpec("Output Float", "Output_7", "VALUE", hide_value=True, default_value=3.0, min_value=1.0, max_value=1.0),
|
||||||
|
SocketSpec("Output Vector", "Output_8", "VECTOR", subtype="EULER", default_value=( 10, 20, 30), min_value=-10.0, max_value=10.0),
|
||||||
|
SocketSpec("Output Color", "Output_9", "RGBA", default_value=(0, 1, 1, 1)),
|
||||||
|
SocketSpec("Output String", "Output_19", "STRING", default_value=""),
|
||||||
|
SocketSpec("Output Bool", "Output_20", "BOOLEAN", default_value=False),
|
||||||
|
SocketSpec("Output Material", "Output_21", "MATERIAL", default_value=bpy.data.materials['TestMaterial']),
|
||||||
|
SocketSpec("Output Int", "Output_22", "INT", default_value=0, min_value=-2147483648, max_value=2147483647),
|
||||||
|
SocketSpec("Output Geometry", "Output_23", "GEOMETRY"),
|
||||||
|
SocketSpec("Output Collection", "Output_24", "COLLECTION", default_value=bpy.data.collections['TestCollection']),
|
||||||
|
SocketSpec("Output Texture", "Output_25", "TEXTURE", default_value=bpy.data.textures['TestTexture']),
|
||||||
|
SocketSpec("Output Object", "Output_26", "OBJECT", default_value=bpy.data.objects['TestObject']),
|
||||||
|
SocketSpec("Output Image", "Output_27", "IMAGE", default_value=bpy.data.images['TestImage']),
|
||||||
|
|
||||||
|
SocketSpec("Input Float", "Input_4", "VALUE", subtype="ANGLE", default_value=-20.0, min_value=5.0, max_value=6.0),
|
||||||
|
SocketSpec("Input Vector", "Input_5", "VECTOR", hide_value=True, default_value=( 2, 4, 6), min_value=-4.0, max_value=100.0),
|
||||||
|
SocketSpec("Input Color", "Input_6", "RGBA", default_value=(0.5, 0.4, 0.3, 0.2)),
|
||||||
|
SocketSpec("Input String", "Input_10", "STRING", default_value="hello world!"),
|
||||||
|
SocketSpec("Input Bool", "Input_11", "BOOLEAN", default_value=True, hide_in_modifier=True),
|
||||||
|
SocketSpec("Input Material", "Input_12", "MATERIAL", default_value=bpy.data.materials['TestMaterial']),
|
||||||
|
SocketSpec("Input Int", "Input_13", "INT", default_value=500, min_value=200, max_value=1000),
|
||||||
|
SocketSpec("Input Geometry", "Input_14", "GEOMETRY"),
|
||||||
|
SocketSpec("Input Collection", "Input_15", "COLLECTION", default_value=bpy.data.collections['TestCollection']),
|
||||||
|
SocketSpec("Input Texture", "Input_16", "TEXTURE", default_value=bpy.data.textures['TestTexture']),
|
||||||
|
SocketSpec("Input Object", "Input_17", "OBJECT", default_value=bpy.data.objects['TestObject']),
|
||||||
|
SocketSpec("Input Image", "Input_18", "IMAGE", default_value=bpy.data.images['TestImage']),
|
||||||
|
])
|
||||||
|
# autopep8: on
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupVersioning25Test(AbstractNodeGroupInterfaceTest):
|
||||||
|
def open_file(self):
|
||||||
|
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "nodegroup25.blend"))
|
||||||
|
self.assertEqual(bpy.data.version, (2, 55, 0))
|
||||||
|
|
||||||
|
def test_load_compositor_nodes(self):
|
||||||
|
self.open_file()
|
||||||
|
|
||||||
|
tree = bpy.data.scenes['Scene'].node_tree
|
||||||
|
group = bpy.data.node_groups.get('NodeGroup.002')
|
||||||
|
self.assertIsNotNone(group, "Compositor node group not found")
|
||||||
|
node = tree.nodes['NodeGroup.002']
|
||||||
|
self.assertEqual(node.node_tree, group, "Node group must use compositor node tree")
|
||||||
|
|
||||||
|
# autopep8: off
|
||||||
|
self.compare_group_to_specs(group, node, [
|
||||||
|
SocketSpec("Image", "Image", "RGBA", default_value=(0, 0, 0, 1)),
|
||||||
|
SocketSpec("Alpha", "Alpha", "VALUE", default_value=1.0, min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("Alpha", "Alpha.001", "VALUE", default_value=0.0, min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("Alpha", "Alpha.002", "VALUE", default_value=0.0, min_value=0.0, max_value=0.0),
|
||||||
|
|
||||||
|
SocketSpec("Fac", "Fac", "VALUE", default_value=0.5, min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("ID value", "ID value", "VALUE", default_value=0.8, min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("ID value", "ID value.001", "VALUE", default_value=0.8, min_value=0.0, max_value=0.0),
|
||||||
|
], test_links=False)
|
||||||
|
# autopep8: on
|
||||||
|
|
||||||
|
def test_load_shader_nodes(self):
|
||||||
|
self.open_file()
|
||||||
|
|
||||||
|
tree = bpy.data.materials['Material'].node_tree
|
||||||
|
group = bpy.data.node_groups.get('NodeGroup')
|
||||||
|
self.assertIsNotNone(group, "Shader node group not found")
|
||||||
|
node = tree.nodes['NodeGroup']
|
||||||
|
self.assertEqual(node.node_tree, group, "Node group must use shader node tree")
|
||||||
|
|
||||||
|
# autopep8: off
|
||||||
|
self.compare_group_to_specs(group, node, [
|
||||||
|
SocketSpec("Color", "Color", "RGBA", default_value=(0, 0, 0, 1)),
|
||||||
|
SocketSpec("Color", "Color.001", "RGBA", default_value=(0, 0, 0, 1)),
|
||||||
|
SocketSpec("Vector", "Vector", "VECTOR", default_value=(0, 0, 0), min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("Value", "Value", "VALUE", default_value=0.0, min_value=0.0, max_value=0.0),
|
||||||
|
|
||||||
|
SocketSpec("Fac", "Fac", "VALUE", default_value=0.5, min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("Color1", "Color1", "RGBA", default_value=(0.5, 0.5, 0.5, 1)),
|
||||||
|
SocketSpec("Color2", "Color2", "RGBA", default_value=(0.5, 0.5, 0.5, 1)),
|
||||||
|
SocketSpec("Fac", "Fac.001", "VALUE", default_value=0.5, min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("Color1", "Color1.001", "RGBA", default_value=(0.5, 0.5, 0.5, 1)),
|
||||||
|
SocketSpec("Color2", "Color2.001", "RGBA", default_value=(0.5, 0.5, 0.5, 1)),
|
||||||
|
SocketSpec("Vector", "Vector", "VECTOR", default_value=(0.5, 0.5, 0.5), min_value=0.0, max_value=0.0),
|
||||||
|
SocketSpec("Vector", "Vector.001", "VECTOR", default_value=(0.5, 0.5, 0.5), min_value=0.0, max_value=0.0),
|
||||||
|
], test_links=False)
|
||||||
|
# autopep8: on
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global args
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
if '--' in sys.argv:
|
||||||
|
argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
|
||||||
|
else:
|
||||||
|
argv = sys.argv
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--testdir', required=True, type=pathlib.Path)
|
||||||
|
args, remaining = parser.parse_known_args(argv)
|
||||||
|
|
||||||
|
unittest.main(argv=remaining)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in New Issue