WIP: Brush assets project #106303

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

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
130 changed files with 1870 additions and 1350 deletions
Showing only changes of commit df8261aa51 - Show all commits

View File

@ -761,6 +761,7 @@ endif()
# Unit testing
option(WITH_GTESTS "Enable GTest unit testing" OFF)
option(WITH_GPU_RENDER_TESTS "Enable GPU render related unit testing (EEVEE, Workbench and Grease Pencil)" OFF)
option(WITH_GPU_RENDER_TESTS_SILENT "Run GPU render tests silently (finished tests will pass). Generated report will show failing tests" ON)
option(WITH_GPU_DRAW_TESTS "Enable GPU drawing related unit testing (GPU backends and draw manager)" OFF)
option(WITH_COMPOSITOR_REALTIME_TESTS "Enable regression testing for realtime compositor" OFF)
if(UNIX AND NOT (APPLE OR HAIKU))

View File

@ -20,6 +20,7 @@ endif()
set(WITH_CYCLES_NATIVE_ONLY ON CACHE BOOL "" FORCE)
set(WITH_DOC_MANPAGE OFF CACHE BOOL "" FORCE)
set(WITH_GTESTS ON CACHE BOOL "" FORCE)
set(WITH_GPU_RENDER_TESTS_SILENT OFF CACHE BOOL "" FORCE)
set(WITH_LIBMV_SCHUR_SPECIALIZATIONS OFF CACHE BOOL "" FORCE)
set(WITH_PYTHON_SAFETY ON CACHE BOOL "" FORCE)
if(WIN32)

View File

@ -18,7 +18,7 @@ if NOT "%1" == "" (
) else if "%1" == "with_tests" (
set TESTS_CMAKE_ARGS=%TESTS_CMAKE_ARGS% -DWITH_GTESTS=On
) else if "%1" == "with_gpu_tests" (
set TESTS_CMAKE_ARGS=%TESTS_CMAKE_ARGS% -DWITH_GPU_DRAW_TESTS=On -DWITH_GPU_RENDER_TESTS=On
set TESTS_CMAKE_ARGS=%TESTS_CMAKE_ARGS% -DWITH_GPU_DRAW_TESTS=On -DWITH_GPU_RENDER_TESTS=On -DWITH_GPU_RENDER_TESTS_SILENT=Off
) else if "%1" == "full" (
set TARGET=Full
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% ^

View File

@ -593,9 +593,8 @@ static inline bool object_use_deform_motion(BL::Object &b_parent, BL::Object &b_
/* If motion blur is enabled for the object we also check
* whether it's enabled for the parent object as well.
*
* This way we can control motion blur from the dupligroup
* duplicator much easier.
*/
* This way we can control motion blur from the dupli-group
* duplicator much easier. */
if (use_deform_motion && b_parent.ptr.data != b_ob.ptr.data) {
PointerRNA parent_cobject = RNA_pointer_get(&b_parent.ptr, "cycles");
use_deform_motion &= get_boolean(parent_cobject, "use_deform_motion");

View File

@ -538,7 +538,7 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
/* TODO: check if the refraction configuration is valid. See `btdf_ggx()` in
* `eevee_bxdf_lib.glsl`. */
float3 H = is_transmission ? -(bsdf->ior * wo + wi) : (wi + wo);
const float inv_len_H = 1.0f / len(H);
const float inv_len_H = safe_divide(1.0f, len(H));
H *= inv_len_H;
/* Compute Fresnel coefficients. */

View File

@ -238,10 +238,17 @@ integrate_direct_light_shadow_init_common(KernelGlobals kg,
/* Path tracing: sample point on light and evaluate light shader, then
* queue shadow ray to be traced. */
template<uint node_feature_mask>
ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const RNGState *rng_state)
#if defined(__KERNEL_GPU__)
ccl_device_forceinline
#else
/* MSVC has very long compilation time (x20) if we force inline this function */
ccl_device
#endif
void
integrate_surface_direct_light(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const RNGState *rng_state)
{
/* Test if there is a light or BSDF that needs direct light. */
if (!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL))) {

View File

@ -47,11 +47,23 @@ ccl_device_forceinline bool osl_closure_skip(KernelGlobals kg,
uint32_t path_flag,
int scattering)
{
/* caustic options */
/* Caustic options */
if ((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) {
if ((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) ||
(!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT)))
{
const bool has_reflect = (scattering & LABEL_REFLECT);
const bool has_transmit = (scattering & LABEL_TRANSMIT);
const bool reflect_caustics_disabled = !kernel_data.integrator.caustics_reflective;
const bool refract_caustics_disabled = !kernel_data.integrator.caustics_refractive;
/* Reflective Caustics */
if (reflect_caustics_disabled && has_reflect && !has_transmit) {
return true;
}
/* Refractive Caustics*/
if (refract_caustics_disabled && has_transmit && !has_reflect) {
return true;
}
/* Glass Caustics */
if (reflect_caustics_disabled && refract_caustics_disabled && has_reflect && has_transmit) {
return true;
}
}
@ -322,7 +334,12 @@ ccl_device void osl_closure_generalized_schlick_bsdf_setup(
const bool has_reflection = !is_zero(closure->reflection_tint);
const bool has_transmission = !is_zero(closure->transmission_tint);
if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) {
int label = LABEL_GLOSSY | LABEL_REFLECT;
if (has_transmission) {
label |= LABEL_TRANSMIT;
}
if (osl_closure_skip(kg, sd, path_flag, label)) {
return;
}

View File

@ -271,64 +271,66 @@ ccl_device
}
/* Metallic component */
if (reflective_caustics && metallic > CLOSURE_WEIGHT_CUTOFF) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), metallic * weight);
ccl_private FresnelF82Tint *fresnel =
(bsdf != NULL) ?
(ccl_private FresnelF82Tint *)closure_alloc_extra(sd, sizeof(FresnelF82Tint)) :
NULL;
if (metallic > CLOSURE_WEIGHT_CUTOFF) {
if (reflective_caustics) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), metallic * weight);
ccl_private FresnelF82Tint *fresnel =
(bsdf != NULL) ?
(ccl_private FresnelF82Tint *)closure_alloc_extra(sd, sizeof(FresnelF82Tint)) :
NULL;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->ior = 1.0f;
bsdf->T = T;
bsdf->alpha_x = alpha_x;
bsdf->alpha_y = alpha_y;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->ior = 1.0f;
bsdf->T = T;
bsdf->alpha_x = alpha_x;
bsdf->alpha_y = alpha_y;
fresnel->f0 = rgb_to_spectrum(clamped_base_color);
const Spectrum f82 = min(specular_tint, one_spectrum());
fresnel->f0 = rgb_to_spectrum(clamped_base_color);
const Spectrum f82 = min(specular_tint, one_spectrum());
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_f82_tint(kg, bsdf, sd, fresnel, f82, is_multiggx);
/* Attenuate other components */
weight *= (1.0f - metallic);
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_f82_tint(kg, bsdf, sd, fresnel, f82, is_multiggx);
}
}
/* Attenuate other components */
weight *= (1.0f - metallic);
}
/* Transmission component */
if (glass_caustics && transmission_weight > CLOSURE_WEIGHT_CUTOFF) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), transmission_weight * weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
if (transmission_weight > CLOSURE_WEIGHT_CUTOFF) {
if (glass_caustics) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), transmission_weight * weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->T = zero_float3();
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->T = zero_float3();
bsdf->alpha_x = bsdf->alpha_y = sqr(roughness);
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / ior : ior;
bsdf->alpha_x = bsdf->alpha_y = sqr(roughness);
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / ior : ior;
fresnel->f0 = make_float3(F0_from_ior(ior)) * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -ior;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = sqrt(rgb_to_spectrum(clamped_base_color));
fresnel->f0 = make_float3(F0_from_ior(ior)) * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -ior;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = sqrt(rgb_to_spectrum(clamped_base_color));
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_generalized_schlick(kg, bsdf, sd, fresnel, is_multiggx);
/* Attenuate other components */
weight *= (1.0f - transmission_weight);
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_generalized_schlick(kg, bsdf, sd, fresnel, is_multiggx);
}
}
/* Attenuate other components */
weight *= (1.0f - transmission_weight);
}
/* Apply IOR adjustment */

View File

@ -8,8 +8,14 @@ CCL_NAMESPACE_BEGIN
/* IES Light */
ccl_device_inline float interpolate_ies_vertical(
KernelGlobals kg, int ofs, int v, int v_num, float v_frac, int h)
ccl_device_inline float interpolate_ies_vertical(KernelGlobals kg,
int ofs,
const bool wrap_vlow,
const bool wrap_vhigh,
int v,
int v_num,
float v_frac,
int h)
{
/* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end
* of v (corresponding to the north pole) would result in artifacts. The proper way of dealing
@ -19,11 +25,22 @@ ccl_device_inline float interpolate_ies_vertical(
* we can just take the corresponding value at the current horizontal coordinate. */
#define IES_LOOKUP(v) kernel_data_fetch(ies, ofs + h * v_num + (v))
/* If v is zero, assume symmetry and read at v=1 instead of v=-1. */
float a = IES_LOOKUP((v == 0) ? 1 : v - 1);
float a = 0.0f;
if (v > 0) {
a = IES_LOOKUP(v - 1);
}
else if (wrap_vlow) {
a = IES_LOOKUP(1);
}
float b = IES_LOOKUP(v);
float c = IES_LOOKUP(v + 1);
float d = IES_LOOKUP(min(v + 2, v_num - 1));
float d = 0.0f;
if (v + 2 < v_num) {
d = IES_LOOKUP(v + 2);
}
else if (wrap_vhigh) {
d = IES_LOOKUP(v_num - 2);
}
#undef IES_LOOKUP
return cubic_interp(a, b, c, d, v_frac);
@ -44,12 +61,21 @@ ccl_device_inline float kernel_ies_interp(KernelGlobals kg, int slot, float h_an
#define IES_LOOKUP_ANGLE_V(v) kernel_data_fetch(ies, ofs + h_num + (v))
/* Check whether the angle is within the bounds of the IES texture. */
if (v_angle < IES_LOOKUP_ANGLE_V(0) || v_angle >= IES_LOOKUP_ANGLE_V(v_num - 1)) {
const float v_low = IES_LOOKUP_ANGLE_V(0), v_high = IES_LOOKUP_ANGLE_V(v_num - 1);
const float h_low = IES_LOOKUP_ANGLE_H(0), h_high = IES_LOOKUP_ANGLE_H(h_num - 1);
if (v_angle < v_low || v_angle >= v_high) {
return 0.0f;
}
if (h_angle < h_low || h_angle >= h_high) {
return 0.0f;
}
kernel_assert(h_angle >= IES_LOOKUP_ANGLE_H(0));
kernel_assert(h_angle <= IES_LOOKUP_ANGLE_H(h_num - 1));
/* If the texture covers the full 360° range horizontally, wrap around the lookup
* to get proper cubic interpolation. Otherwise, just set the out-of-range values to zero.
* Similar logic for V, but there we check the lower and upper wrap separately. */
const bool wrap_h = (h_low < 1e-7f && h_high > M_2PI_F - 1e-7f);
const bool wrap_vlow = (v_low < 1e-7f);
const bool wrap_vhigh = (v_high > M_PI_F - 1e-7f);
/* Lookup the angles to find the table position. */
int h_i, v_i;
@ -71,17 +97,24 @@ ccl_device_inline float kernel_ies_interp(KernelGlobals kg, int slot, float h_an
/* Skip forward to the actual intensity data. */
ofs += h_num + v_num;
/* Perform cubic interpolation along the horizontal coordinate to get the intensity value.
* If h_i is zero, just wrap around since the horizontal angles always go over the full circle.
* However, the last entry (360°) equals the first one, so we need to wrap around to the one
* before that. */
float a = interpolate_ies_vertical(
kg, ofs, v_i, v_num, v_frac, (h_i == 0) ? h_num - 2 : h_i - 1);
float b = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i);
float c = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i + 1);
/* Same logic here, wrap around to the second element if necessary. */
float d = interpolate_ies_vertical(
kg, ofs, v_i, v_num, v_frac, (h_i + 2 == h_num) ? 1 : h_i + 2);
float a = 0.0f;
if (h_i > 0) {
a = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i - 1);
}
else if (wrap_h) {
/* The last entry (360°) equals the first one, so we need to wrap around to the one before. */
a = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_num - 2);
}
float b = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i);
float c = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i + 1);
float d = 0.0f;
if (h_i + 2 < h_num) {
d = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i + 2);
}
else if (wrap_h) {
/* Same logic here, wrap around to the second element if necessary. */
d = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, 1);
}
/* Cubic interpolation can result in negative values, so get rid of them. */
return max(cubic_interp(a, b, c, d, h_frac), 0.0f);

View File

@ -154,11 +154,7 @@ bool IESFile::parse(const string &ies)
int h_angles_num = parser.get_long(); /* Number of horizontal angles */
type = (IESType)parser.get_long(); /* Photometric type */
/* TODO(lukas): Test whether the current type B processing can also deal with type A files.
* In theory the only difference should be orientation which we ignore anyways, but with IES you
* never know...
*/
if (type != TYPE_B && type != TYPE_C) {
if (type != TYPE_A && type != TYPE_B && type != TYPE_C) {
return false;
}
@ -205,8 +201,26 @@ bool IESFile::parse(const string &ies)
return !parser.has_error();
}
bool IESFile::process_type_b()
static bool angle_close(float a, float b)
{
return fabsf(a - b) < 1e-4f;
}
/* Processing functions to turn file contents into the format that Cycles expects.
* Handles type conversion (the output format is based on Type C), symmetry/mirroring,
* value shifting etc.
* Note that this code is much more forgiving than the spec. For example, in type A and B,
* the range of vertical angles officially must be either exactly 0°-90° or -90°-90°.
* However, in practise, IES files are all over the place. Therefore, the handling is as
* flexible as possible, and tries to turn any input into something useful. */
void IESFile::process_type_b()
{
/* According to the standard, Type B defines a different coordinate system where the polar axis
* is horizontal, not vertical.
* To avoid overcomplicating the conversion logic, we just transpose the angles and use the
* regular Type A/C coordinate system. Users can just rotate the light to get the "proper"
* orientation. */
vector<vector<float>> newintensity;
newintensity.resize(v_angles.size());
for (int i = 0; i < v_angles.size(); i++) {
@ -218,14 +232,8 @@ bool IESFile::process_type_b()
intensity.swap(newintensity);
h_angles.swap(v_angles);
float h_first = h_angles[0], h_last = h_angles[h_angles.size() - 1];
if (h_last != 90.0f) {
return false;
}
if (h_first == 0.0f) {
/* The range in the file corresponds to 90°-180°, we need to mirror that to get the
* full 180° range. */
if (angle_close(h_angles[0], 0.0f)) {
/* File angles cover 0°-90°. Mirror that to -90°-90°, and shift to 0°-180° to match Cycles. */
vector<float> new_h_angles;
vector<vector<float>> new_intensity;
int hnum = h_angles.size();
@ -242,26 +250,15 @@ bool IESFile::process_type_b()
h_angles.swap(new_h_angles);
intensity.swap(new_intensity);
}
else if (h_first == -90.0f) {
/* We have full 180° coverage, so just shift to match the angle range convention. */
else {
/* File angles cover -90°-90°. Shift to 0°-180° to match Cycles. */
for (int i = 0; i < h_angles.size(); i++) {
h_angles[i] += 90.0f;
}
}
/* To get correct results with the cubic interpolation in the kernel, the horizontal range
* has to cover all 360°. Therefore, we copy the 0° entry to 360° to ensure full coverage
* and seamless interpolation. */
h_angles.push_back(360.0f);
intensity.push_back(intensity[0]);
float v_first = v_angles[0], v_last = v_angles[v_angles.size() - 1];
if (v_last != 90.0f) {
return false;
}
if (v_first == 0.0f) {
/* The range in the file corresponds to 90°-180°, we need to mirror that to get the
* full 180° range. */
if (angle_close(v_angles[0], 0.0f)) {
/* File angles cover 0°-90°. Mirror that to -90°-90°, and shift to 0°-180° to match Cycles. */
vector<float> new_v_angles;
int hnum = h_angles.size();
int vnum = v_angles.size();
@ -275,7 +272,7 @@ bool IESFile::process_type_b()
for (int i = 0; i < hnum; i++) {
vector<float> new_intensity;
new_intensity.reserve(2 * vnum - 1);
for (int j = vnum - 2; j >= 0; j--) {
for (int j = vnum - 1; j > 0; j--) {
new_intensity.push_back(intensity[i][j]);
}
new_intensity.insert(new_intensity.end(), intensity[i].begin(), intensity[i].end());
@ -283,36 +280,64 @@ bool IESFile::process_type_b()
}
v_angles.swap(new_v_angles);
}
else if (v_first == -90.0f) {
/* We have full 180° coverage, so just shift to match the angle range convention. */
else {
/* File angles cover -90°-90°. Shift to 0°-180° to match Cycles. */
for (int i = 0; i < v_angles.size(); i++) {
v_angles[i] += 90.0f;
}
}
return true;
}
bool IESFile::process_type_c()
void IESFile::process_type_a()
{
if (h_angles[0] == 90.0f) {
/* Some files are stored from 90° to 270°, so we just rotate them to the regular 0°-180° range
* here. */
/* Convert vertical angles - just a simple offset. */
for (int i = 0; i < v_angles.size(); i++) {
v_angles[i] += 90.0f;
}
vector<float> new_h_angles;
new_h_angles.reserve(h_angles.size());
vector<vector<float>> new_intensity;
new_intensity.reserve(h_angles.size());
/* Type A goes from -90° to 90°, which is mapped to 270° to 90° respectively in Type C. */
for (int i = h_angles.size() - 1; i >= 0; i--) {
new_h_angles.push_back(180.0f - h_angles[i]);
new_intensity.push_back(intensity[i]);
}
/* If the file angles start at 0°, we need to mirror around that.
* Since the negative input range (which we generate here) maps to 180° to 270°,
* it comes after the original entries in the output. */
if (angle_close(h_angles[0], 0.0f)) {
new_h_angles.reserve(2 * h_angles.size() - 1);
new_intensity.reserve(2 * h_angles.size() - 1);
for (int i = 1; i < h_angles.size(); i++) {
new_h_angles.push_back(180.0f + h_angles[i]);
new_intensity.push_back(intensity[i]);
}
}
h_angles.swap(new_h_angles);
intensity.swap(new_intensity);
}
void IESFile::process_type_c()
{
if (angle_close(h_angles[0], 90.0f)) {
/* Some files are stored from 90° to 270°, so rotate them to the regular 0°-180° range. */
for (int i = 0; i < h_angles.size(); i++) {
h_angles[i] -= 90.0f;
}
}
if (h_angles[0] != 0.0f) {
return false;
}
if (h_angles.size() == 1) {
h_angles[0] = 0.0f;
h_angles.push_back(360.0f);
intensity.push_back(intensity[0]);
}
if (h_angles[h_angles.size() - 1] == 90.0f) {
if (angle_close(h_angles[h_angles.size() - 1], 90.0f)) {
/* Only one quadrant is defined, so we need to mirror twice (from one to two, then to four).
* Since the two->four mirroring step might also be required if we get an input of two
* quadrants, we only do the first mirror here and later do the second mirror in either case.
@ -324,7 +349,7 @@ bool IESFile::process_type_c()
}
}
if (h_angles[h_angles.size() - 1] == 180.0f) {
if (angle_close(h_angles[h_angles.size() - 1], 180.0f)) {
/* Mirror half to the full range. */
int hnum = h_angles.size();
for (int i = hnum - 2; i >= 0; i--) {
@ -335,31 +360,16 @@ bool IESFile::process_type_c()
/* Some files skip the 360° entry (contrary to standard) because it's supposed to be identical to
* the 0° entry. If the file has a discernible order in its spacing, just fix this. */
if (h_angles[h_angles.size() - 1] != 360.0f) {
if (angle_close(h_angles[0], 0.0f) && !angle_close(h_angles[h_angles.size() - 1], 360.0f)) {
int hnum = h_angles.size();
float last_step = h_angles[hnum - 1] - h_angles[hnum - 2];
float first_step = h_angles[1] - h_angles[0];
float difference = 360.0f - h_angles[hnum - 1];
if (last_step == difference || first_step == difference) {
float gap_step = 360.0f - h_angles[hnum - 1];
if (angle_close(last_step, gap_step) || angle_close(first_step, gap_step)) {
h_angles.push_back(360.0f);
intensity.push_back(intensity[0]);
}
else {
return false;
}
}
float v_first = v_angles[0], v_last = v_angles[v_angles.size() - 1];
if (v_first == 90.0f) {
if (v_last != 180.0f) {
return false;
}
}
else if (v_first != 0.0f) {
return false;
}
return true;
}
bool IESFile::process()
@ -368,22 +378,19 @@ bool IESFile::process()
return false;
}
if (type == TYPE_B) {
if (!process_type_b()) {
return false;
}
if (type == TYPE_A) {
process_type_a();
}
else if (type == TYPE_B) {
process_type_b();
}
else if (type == TYPE_C) {
process_type_c();
}
else {
assert(type == TYPE_C);
if (!process_type_c()) {
return false;
}
return false;
}
assert(v_angles[0] == 0.0f || v_angles[0] == 90.0f);
assert(h_angles[0] == 0.0f);
assert(h_angles[h_angles.size() - 1] == 360.0f);
/* Convert from deg to rad. */
for (int i = 0; i < v_angles.size(); i++) {
v_angles[i] *= M_PI_F / 180.f;

View File

@ -24,8 +24,9 @@ class IESFile {
protected:
bool parse(const string &ies);
bool process();
bool process_type_b();
bool process_type_c();
void process_type_a();
void process_type_b();
void process_type_c();
/* The brightness distribution is stored in spherical coordinates.
* The horizontal angles correspond to theta in the regular notation
@ -36,7 +37,7 @@ class IESFile {
* of one horizontal segment. */
vector<vector<float>> intensity;
/* Types of angle representation in IES files. Currently, only B and C are supported. */
/* Types of angle representation in IES files. */
enum IESType { TYPE_A = 3, TYPE_B = 2, TYPE_C = 1 } type;
};

View File

@ -83,8 +83,8 @@ extern GHOST_TSuccess GHOST_DisposeEventConsumer(GHOST_EventConsumerHandle consu
/**
* Returns the system time.
* Returns the number of milliseconds since the start of the system process.
* Based on ANSI clock() routine.
* Returns the number of milliseconds since the start of the system.
*
* \param systemhandle: The handle to the system.
* \return The number of milliseconds.
*/

View File

@ -170,8 +170,10 @@ class GHOST_ISystem {
/**
* Returns the system time.
* Returns the number of milliseconds since the start of the system process.
* Based on ANSI clock() routine.
* Returns the number of milliseconds since the start of the system.
* \note The exact method used is platform dependent however monotonic methods should be used
* instead of wall-clock time.
*
* \return The number of milliseconds.
*/
virtual uint64_t getMilliSeconds() const = 0;

View File

@ -802,5 +802,5 @@ void GHOST_SystemSDL::putClipboard(const char *buffer, bool /*selection*/) const
uint64_t GHOST_SystemSDL::getMilliSeconds() const
{
return uint64_t(SDL_GetTicks()); /* NOTE: 32 -> 64bits. */
return SDL_GetTicks64();
}

View File

@ -106,7 +106,6 @@ using namespace std;
GHOST_SystemX11::GHOST_SystemX11()
: GHOST_System(),
m_xkb_descr(nullptr),
m_start_time(0),
m_keyboard_vector{0},
#ifdef WITH_X11_XINPUT
m_last_key_time(0),
@ -175,14 +174,6 @@ GHOST_SystemX11::GHOST_SystemX11()
m_last_release_keycode = 0;
m_last_release_time = 0;
{
timespec ts = {0, 0};
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
GHOST_ASSERT(false, "Could not instantiate monotonic timer!");
}
m_start_time = (uint64_t(ts.tv_sec) * 1000) + uint64_t(ts.tv_nsec / 1000000);
}
/* Use detectable auto-repeat, mac and windows also do this. */
int use_xkb;
int xkb_opcode, xkb_event, xkb_error;
@ -278,8 +269,7 @@ uint64_t GHOST_SystemX11::getMilliSeconds() const
}
/* Taking care not to overflow the tv.tv_sec * 1000 */
const uint64_t time = (uint64_t(ts.tv_sec) * 1000) + uint64_t(ts.tv_nsec / 1000000);
GHOST_ASSERT(m_start_time <= time, "Monotonic time unexpectedly went backwards!");
return time - m_start_time;
return time;
}
uint64_t GHOST_SystemX11::ms_from_input_time(Time timestamp) const
@ -290,7 +280,7 @@ uint64_t GHOST_SystemX11::ms_from_input_time(Time timestamp) const
* XORG/LIBINPUT which uses time-stamps based on the monotonic time,
* Needed to resolve failure to detect double-clicking, see: #40009. */
/* Accumulate time rollover (as well as store the initial delta from `m_start_time`). */
/* Accumulate time rollover (as well as store the initial delta from #getMilliSeconds). */
static uint64_t timestamp_offset = 0;
/* The last event time (to detect rollover). */

View File

@ -348,9 +348,6 @@ class GHOST_SystemX11 : public GHOST_System {
/** The vector of windows that need to be updated. */
std::vector<GHOST_WindowX11 *> m_dirty_windows;
/** Start time at initialization (using `CLOCK_MONOTONIC`). */
uint64_t m_start_time;
/** A vector of keyboard key masks. */
char m_keyboard_vector[32];

View File

@ -543,11 +543,11 @@ class NODE_MT_category_GEO_UTILITIES(Menu):
layout.menu("NODE_MT_category_GEO_UTILITIES_MATH")
layout.menu("NODE_MT_category_GEO_UTILITIES_ROTATION")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeIndexSwitch")
node_add_menu.add_node_type(layout, "GeometryNodeMenuSwitch")
node_add_menu.add_node_type(layout, "FunctionNodeRandomValue")
node_add_menu.add_repeat_zone(layout, label="Repeat Zone")
node_add_menu.add_node_type(layout, "GeometryNodeSwitch")
node_add_menu.add_node_type(layout, "GeometryNodeIndexSwitch")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)

View File

@ -59,7 +59,13 @@ class DATA_PT_curves_surface(DataButtonsPanel, Panel):
layout.prop(ob.data, "surface")
has_surface = ob.data.surface is not None
if has_surface:
layout.prop_search(ob.data, "surface_uv_map", ob.data.surface.data, "uv_layers", text="UV Map", icon='GROUP_UVS')
layout.prop_search(
ob.data,
"surface_uv_map",
ob.data.surface.data,
"uv_layers",
text="UV Map",
icon='GROUP_UVS')
else:
row = layout.row()
row.prop(ob.data, "surface_uv_map", text="UV Map")

View File

@ -188,8 +188,9 @@ class OBJECT_MT_modifier_add_deform(ModifierAddMenu, Menu):
if ob_type == 'VOLUME':
self.operator_modifier_add(layout, 'VOLUME_DISPLACE')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SMOOTH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET')
self.operator_modifier_add(layout, 'GREASE_PENCIL_NOISE')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)

View File

@ -8510,7 +8510,7 @@ class VIEW3D_PT_sculpt_automasking(Panel):
col.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary")
if sculpt.use_automasking_boundary_edges or sculpt.use_automasking_boundary_face_sets:
col.prop(sculpt.brush, "automasking_boundary_edges_propagation_steps")
col.prop(sculpt, "automasking_boundary_edges_propagation_steps")
col.separator()

View File

@ -8,6 +8,8 @@
* \brief Functions to work with AnimData.
*/
#pragma once
struct ID;
struct Main;

View File

@ -7,16 +7,22 @@
*/
#include "ANIM_animdata.hh"
#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_fcurve.h"
#include "BKE_lib_id.hh"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "DNA_anim_types.h"
#include "ED_anim_api.hh"
#include "RNA_access.hh"
#include "RNA_path.hh"

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 15
#define BLENDER_FILE_SUBVERSION 16
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -413,7 +413,7 @@ class Layer : public ::GreasePencilLayer {
bool has_drawing_at(const int frame_number) const;
/**
* \returns the key of the active frame at \a frame_number or std::nullopt if there is no frame.
* \returns the key of the active frame at \a frame_number or #std::nullopt if there is no frame.
*/
std::optional<FramesMapKey> frame_key_at(int frame_number) const;
@ -438,12 +438,12 @@ class Layer : public ::GreasePencilLayer {
void tag_frames_map_keys_changed();
/**
* Prepare the DNA #GreasePencilLayer data before blendfile writing.
* Prepare the DNA #GreasePencilLayer data before blend-file writing.
*/
void prepare_for_dna_write();
/**
* Update from DNA #GreasePencilLayer data after blendfile reading.
* Update from DNA #GreasePencilLayer data after blend-file reading.
*/
void update_from_dna_read();
@ -543,12 +543,12 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
void print_nodes(StringRefNull header) const;
/**
* Prepare the DNA #GreasePencilLayerTreeGroup data before blendfile writing.
* Prepare the DNA #GreasePencilLayerTreeGroup data before blend-file writing.
*/
void prepare_for_dna_write();
/**
* Update from DNA #GreasePencilLayerTreeGroup data after blendfile reading.
* Update from DNA #GreasePencilLayerTreeGroup data after blend-file reading.
*/
void update_from_dna_read();

View File

@ -73,7 +73,7 @@ void BKE_view_layer_free_ex(ViewLayer *view_layer, bool do_id_user);
/**
* Free the bases of this #ViewLayer, and what they reference.
* This includes baseact, object_bases, object_bases_hash, and layer_collections.
* This includes `baseact`, `object_bases`, `object_bases_hash`, and `layer_collections`.
*/
void BKE_view_layer_free_object_content(ViewLayer *view_layer);

View File

@ -29,7 +29,7 @@ blender::MutableSpan<NodeEnumItem> NodeEnumDefinition::items_for_write()
return {this->items_array, this->items_num};
}
NodeEnumItem *NodeEnumDefinition::add_item(blender::StringRef name)
NodeEnumItem *NodeEnumDefinition::add_item(const blender::StringRef name)
{
const int insert_index = this->items_num;
NodeEnumItem *old_items = this->items_array;
@ -82,11 +82,11 @@ void NodeEnumDefinition::clear()
this->active_index = int16_t(active_index);
}
bool NodeEnumDefinition::move_item(uint16_t from_index, uint16_t to_index)
bool NodeEnumDefinition::move_item(const int from_index, const int to_index)
{
if (to_index < this->items_num) {
int items_num = this->items_num;
int active_index = this->active_index;
const int items_num = this->items_num;
const int active_index = this->active_index;
blender::dna::array::move_index(this->items_array, items_num, from_index, to_index);
this->items_num = int16_t(items_num);
this->active_index = int16_t(active_index);
@ -115,7 +115,7 @@ void NodeEnumDefinition::active_item_set(NodeEnumItem *item)
this->active_index = this->items().contains_ptr(item) ? item - this->items_array : -1;
}
void NodeEnumDefinition::set_item_name(NodeEnumItem &item, blender::StringRef name)
void NodeEnumDefinition::set_item_name(NodeEnumItem &item, const blender::StringRef name)
{
char unique_name[MAX_NAME + 4];
STRNCPY(unique_name, name.data());

View File

@ -822,6 +822,8 @@ class NodeTreeMainUpdater {
bNodeSocket &input = *node->input_sockets()[0];
BLI_assert(input.is_available() && input.type == SOCK_MENU);
this->set_enum_ptr(*input.default_value_typed<bNodeSocketValueMenu>(), enum_items);
/* Remove initial user. */
enum_items->remove_user_and_delete_if_last();
}
continue;
}
@ -841,12 +843,15 @@ class NodeTreeMainUpdater {
/* Propagate enum references from output links. */
for (bNodeSocket *output : node->output_sockets()) {
if (output->is_available() && output->type == SOCK_MENU) {
for (const bNodeSocket *input : output->directly_linked_sockets()) {
this->update_socket_enum_definition(
*output->default_value_typed<bNodeSocketValueMenu>(),
*input->default_value_typed<bNodeSocketValueMenu>());
if (!output->is_available() || output->type != SOCK_MENU) {
continue;
}
for (const bNodeSocket *input : output->directly_linked_sockets()) {
if (!input->is_available() || input->type != SOCK_MENU) {
continue;
}
this->update_socket_enum_definition(*output->default_value_typed<bNodeSocketValueMenu>(),
*input->default_value_typed<bNodeSocketValueMenu>());
}
}

View File

@ -57,7 +57,7 @@ typedef struct BlendFileData {
int fileflags;
int globalf;
/** Typically the actual filepath of the read blendfile, except when recovering
/** Typically the actual filepath of the read blend-file, except when recovering
* save-on-exit/autosave files. In the latter case, it will be the path of the file that
* generated the auto-saved one being recovered.
*

View File

@ -67,7 +67,7 @@ struct FileData {
/** Used for relative paths handling.
*
* Typically the actual filepath of the read blendfile, except when recovering
* Typically the actual filepath of the read blend-file, except when recovering
* save-on-exit/autosave files. In the latter case, it will be the path of the file that
* generated the auto-saved one being recovered.
*

View File

@ -1542,16 +1542,72 @@ static void version_principled_bsdf_specular_tint(bNodeTree *ntree)
}
bNodeSocket *base_color_sock = nodeFindSocket(node, SOCK_IN, "Base Color");
bNodeSocket *metallic_sock = nodeFindSocket(node, SOCK_IN, "Metallic");
float specular_tint_old = *version_cycles_node_socket_float_value(specular_tint_sock);
float *base_color = version_cycles_node_socket_rgba_value(base_color_sock);
float metallic = *version_cycles_node_socket_float_value(metallic_sock);
/* Change socket type to Color. */
nodeModifySocketTypeStatic(ntree, node, specular_tint_sock, SOCK_RGBA, 0);
float *specular_tint = version_cycles_node_socket_rgba_value(specular_tint_sock);
/* The conversion logic here is that the new Specular Tint should be
* mix(one, mix(base_color, one, metallic), old_specular_tint).
* This needs to be handled both for the fixed values, as well as for any potential connected
* inputs. */
static float one[] = {1.0f, 1.0f, 1.0f, 1.0f};
/* Add a mix node when working with dynamic inputs. */
if (specular_tint_sock->link || (base_color_sock->link && specular_tint_old != 0)) {
/* Mix the fixed values. */
float metallic_mix[4];
interp_v4_v4v4(metallic_mix, base_color, one, metallic);
interp_v4_v4v4(specular_tint, one, metallic_mix, specular_tint_old);
if (specular_tint_sock->link == nullptr && specular_tint_old <= 0.0f) {
/* Specular Tint was fixed at zero, we don't need any conversion node setup. */
continue;
}
/* If the Metallic input is dynamic, or fixed > 0 and base color is dynamic,
* we need to insert a node to compute the metallic_mix.
* Otherwise, use whatever is connected to the base color, or the static value
* if it's unconnected. */
bNodeSocket *metallic_mix_out = nullptr;
bNode *metallic_mix_node = nullptr;
if (metallic_sock->link || (base_color_sock->link && metallic > 0.0f)) {
/* Metallic Mix needs to be dynamically mixed. */
bNode *mix = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX);
static_cast<NodeShaderMix *>(mix->storage)->data_type = SOCK_RGBA;
mix->locx = node->locx - 270;
mix->locy = node->locy - 120;
bNodeSocket *a_in = nodeFindSocket(mix, SOCK_IN, "A_Color");
bNodeSocket *b_in = nodeFindSocket(mix, SOCK_IN, "B_Color");
bNodeSocket *fac_in = nodeFindSocket(mix, SOCK_IN, "Factor_Float");
metallic_mix_out = nodeFindSocket(mix, SOCK_OUT, "Result_Color");
metallic_mix_node = mix;
copy_v4_v4(version_cycles_node_socket_rgba_value(a_in), base_color);
if (base_color_sock->link) {
nodeAddLink(
ntree, base_color_sock->link->fromnode, base_color_sock->link->fromsock, mix, a_in);
}
copy_v4_v4(version_cycles_node_socket_rgba_value(b_in), one);
*version_cycles_node_socket_float_value(fac_in) = metallic;
if (metallic_sock->link) {
nodeAddLink(
ntree, metallic_sock->link->fromnode, metallic_sock->link->fromsock, mix, fac_in);
}
}
else if (base_color_sock->link) {
/* Metallic Mix is a no-op and equivalent to Base Color*/
metallic_mix_out = base_color_sock->link->fromsock;
metallic_mix_node = base_color_sock->link->fromnode;
}
/* Similar to above, if the Specular Tint input is dynamic, or fixed > 0 and metallic mix
* is dynamic, we need to insert a node to compute the new specular tint. */
if (specular_tint_sock->link || (metallic_mix_out && specular_tint_old > 0.0f)) {
bNode *mix = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX);
static_cast<NodeShaderMix *>(mix->storage)->data_type = SOCK_RGBA;
mix->locx = node->locx - 170;
@ -1563,13 +1619,11 @@ static void version_principled_bsdf_specular_tint(bNodeTree *ntree)
bNodeSocket *result_out = nodeFindSocket(mix, SOCK_OUT, "Result_Color");
copy_v4_v4(version_cycles_node_socket_rgba_value(a_in), one);
copy_v4_v4(version_cycles_node_socket_rgba_value(b_in), base_color);
*version_cycles_node_socket_float_value(fac_in) = specular_tint_old;
if (base_color_sock->link) {
nodeAddLink(
ntree, base_color_sock->link->fromnode, base_color_sock->link->fromsock, mix, b_in);
copy_v4_v4(version_cycles_node_socket_rgba_value(b_in), metallic_mix);
if (metallic_mix_out) {
nodeAddLink(ntree, metallic_mix_node, metallic_mix_out, mix, b_in);
}
*version_cycles_node_socket_float_value(fac_in) = specular_tint_old;
if (specular_tint_sock->link) {
nodeAddLink(ntree,
specular_tint_sock->link->fromnode,
@ -1580,10 +1634,6 @@ static void version_principled_bsdf_specular_tint(bNodeTree *ntree)
}
nodeAddLink(ntree, mix, result_out, node, specular_tint_sock);
}
float *specular_tint = version_cycles_node_socket_rgba_value(specular_tint_sock);
/* Mix the fixed values. */
interp_v4_v4v4(specular_tint, one, base_color, specular_tint_old);
}
}
@ -2728,6 +2778,17 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 401, 16)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Sculpt *sculpt = scene->toolsettings->sculpt;
if (sculpt != nullptr) {
Sculpt default_sculpt = *DNA_struct_default_get(Sculpt);
sculpt->automasking_boundary_edges_propagation_steps =
default_sculpt.automasking_boundary_edges_propagation_steps;
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@ -383,6 +383,10 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene)
if (idprop) {
IDP_ClearProperty(idprop);
}
if (ts->sculpt) {
ts->sculpt->automasking_boundary_edges_propagation_steps = 1;
}
}
void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)

View File

@ -587,9 +587,6 @@ if(WITH_COMPOSITOR_CPU)
operations/COM_MovieClipOperation.cc
operations/COM_MovieClipOperation.h
operations/COM_AntiAliasOperation.cc
operations/COM_AntiAliasOperation.h
operations/COM_MaskOperation.cc
operations/COM_MaskOperation.h
)

View File

@ -3,10 +3,10 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "COM_DilateErodeNode.h"
#include "COM_AntiAliasOperation.h"
#include "COM_DilateErodeOperation.h"
#include "COM_GaussianAlphaXBlurOperation.h"
#include "COM_GaussianAlphaYBlurOperation.h"
#include "COM_SMAAOperation.h"
namespace blender::compositor {
@ -38,11 +38,26 @@ void DilateErodeNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
if (editor_node->custom3 < 2.0f) {
AntiAliasOperation *anti_alias = new AntiAliasOperation();
converter.add_operation(anti_alias);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(operation->get_output_socket(), anti_alias->get_input_socket(0));
converter.map_output_socket(get_output_socket(0), anti_alias->get_output_socket(0));
converter.add_link(operation->get_output_socket(), smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood =
new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(operation->get_output_socket(), smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(get_output_socket(0), smaa_neighborhood->get_output_socket());
}
else {
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));

View File

@ -4,8 +4,8 @@
#include "COM_ZCombineNode.h"
#include "COM_AntiAliasOperation.h"
#include "COM_MathBaseOperation.h"
#include "COM_SMAAOperation.h"
#include "COM_ZCombineOperation.h"
namespace blender::compositor {
@ -54,11 +54,25 @@ void ZCombineNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(get_input_socket(3), maskoperation->get_input_socket(1));
/* Step 2 anti alias mask bit of an expensive operation, but does the trick. */
AntiAliasOperation *antialiasoperation = new AntiAliasOperation();
converter.add_operation(antialiasoperation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(maskoperation->get_output_socket(),
antialiasoperation->get_input_socket(0));
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(maskoperation->get_output_socket(), smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
/* use mask to blend between the input colors. */
ZCombineMaskOperation *zcombineoperation = this->get_bnode()->custom1 ?
@ -66,7 +80,7 @@ void ZCombineNode::convert_to_operations(NodeConverter &converter,
new ZCombineMaskOperation();
converter.add_operation(zcombineoperation);
converter.add_link(antialiasoperation->get_output_socket(),
converter.add_link(smaa_neighborhood->get_output_socket(),
zcombineoperation->get_input_socket(0));
converter.map_input_socket(get_input_socket(0), zcombineoperation->get_input_socket(1));
converter.map_input_socket(get_input_socket(2), zcombineoperation->get_input_socket(2));

View File

@ -1,258 +0,0 @@
/* SPDX-FileCopyrightText: 2011 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "COM_AntiAliasOperation.h"
namespace blender::compositor {
/* An implementation of the Scale3X edge-extrapolation algorithm.
*
* Code from GIMP plugin, based on code from Adam D. Moss <adam@gimp.org>
* licensed by the MIT license.
*/
static int extrapolate9(float *E0,
float *E1,
float *E2,
float *E3,
float *E4,
float *E5,
float *E6,
float *E7,
float *E8,
const float *A,
const float *B,
const float *C,
const float *D,
const float *E,
const float *F,
const float *G,
const float *H,
const float *I)
{
#define PEQ(X, Y) (fabsf(*X - *Y) < 1e-3f)
#define PCPY(DST, SRC) \
do { \
*DST = *SRC; \
} while (0)
if (!PEQ(B, H) && !PEQ(D, F)) {
if (PEQ(D, B)) {
PCPY(E0, D);
}
else {
PCPY(E0, E);
}
if ((PEQ(D, B) && !PEQ(E, C)) || (PEQ(B, F) && !PEQ(E, A))) {
PCPY(E1, B);
}
else {
PCPY(E1, E);
}
if (PEQ(B, F)) {
PCPY(E2, F);
}
else {
PCPY(E2, E);
}
if ((PEQ(D, B) && !PEQ(E, G)) || (PEQ(D, H) && !PEQ(E, A))) {
PCPY(E3, D);
}
else {
PCPY(E3, E);
}
PCPY(E4, E);
if ((PEQ(B, F) && !PEQ(E, I)) || (PEQ(H, F) && !PEQ(E, C))) {
PCPY(E5, F);
}
else {
PCPY(E5, E);
}
if (PEQ(D, H)) {
PCPY(E6, D);
}
else {
PCPY(E6, E);
}
if ((PEQ(D, H) && !PEQ(E, I)) || (PEQ(H, F) && !PEQ(E, G))) {
PCPY(E7, H);
}
else {
PCPY(E7, E);
}
if (PEQ(H, F)) {
PCPY(E8, F);
}
else {
PCPY(E8, E);
}
return 1;
}
return 0;
#undef PEQ
#undef PCPY
}
AntiAliasOperation::AntiAliasOperation()
{
this->add_input_socket(DataType::Value);
this->add_output_socket(DataType::Value);
value_reader_ = nullptr;
flags_.complex = true;
flags_.can_be_constant = true;
}
void AntiAliasOperation::init_execution()
{
value_reader_ = this->get_input_socket_reader(0);
}
void AntiAliasOperation::execute_pixel(float output[4], int x, int y, void *data)
{
MemoryBuffer *input_buffer = (MemoryBuffer *)data;
const int buffer_width = input_buffer->get_width(), buffer_height = input_buffer->get_height();
if (y < 0 || y >= buffer_height || x < 0 || x >= buffer_width) {
output[0] = 0.0f;
}
else {
const float *buffer = input_buffer->get_buffer();
const float *row_curr = &buffer[y * buffer_width];
if (x == 0 || x == buffer_width - 1 || y == 0 || y == buffer_height - 1) {
output[0] = row_curr[x];
return;
}
const float *row_prev = &buffer[(y - 1) * buffer_width],
*row_next = &buffer[(y + 1) * buffer_width];
float ninepix[9];
if (extrapolate9(&ninepix[0],
&ninepix[1],
&ninepix[2],
&ninepix[3],
&ninepix[4],
&ninepix[5],
&ninepix[6],
&ninepix[7],
&ninepix[8],
&row_prev[x - 1],
&row_prev[x],
&row_prev[x + 1],
&row_curr[x - 1],
&row_curr[x],
&row_curr[x + 1],
&row_next[x - 1],
&row_next[x],
&row_next[x + 1]))
{
/* Some rounding magic to so make weighting correct with the
* original coefficients.
*/
uchar result = ((3 * ninepix[0] + 5 * ninepix[1] + 3 * ninepix[2] + 5 * ninepix[3] +
6 * ninepix[4] + 5 * ninepix[5] + 3 * ninepix[6] + 5 * ninepix[7] +
3 * ninepix[8]) *
255.0f +
19.0f) /
38.0f;
output[0] = result / 255.0f;
}
else {
output[0] = row_curr[x];
}
}
}
void AntiAliasOperation::deinit_execution()
{
value_reader_ = nullptr;
}
bool AntiAliasOperation::determine_depending_area_of_interest(rcti *input,
ReadBufferOperation *read_operation,
rcti *output)
{
rcti image_input;
NodeOperation *operation = get_input_operation(0);
image_input.xmax = input->xmax + 1;
image_input.xmin = input->xmin - 1;
image_input.ymax = input->ymax + 1;
image_input.ymin = input->ymin - 1;
return operation->determine_depending_area_of_interest(&image_input, read_operation, output);
}
void *AntiAliasOperation::initialize_tile_data(rcti *rect)
{
return get_input_operation(0)->initialize_tile_data(rect);
}
void AntiAliasOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
r_input_area.xmax = output_area.xmax + 1;
r_input_area.xmin = output_area.xmin - 1;
r_input_area.ymax = output_area.ymax + 1;
r_input_area.ymin = output_area.ymin - 1;
}
void AntiAliasOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input = inputs[0];
const rcti &input_area = input->get_rect();
float ninepix[9];
for (int y = area.ymin; y < area.ymax; y++) {
float *out = output->get_elem(area.xmin, y);
const float *row_curr = input->get_elem(area.xmin, y);
const float *row_prev = row_curr - input->row_stride;
const float *row_next = row_curr + input->row_stride;
int x_offset = 0;
for (int x = area.xmin; x < area.xmax;
x++, out += output->elem_stride, x_offset += input->elem_stride)
{
if (x == input_area.xmin || x == input_area.xmax - 1 || y == input_area.xmin ||
y == input_area.ymax - 1)
{
out[0] = row_curr[x_offset];
continue;
}
if (extrapolate9(&ninepix[0],
&ninepix[1],
&ninepix[2],
&ninepix[3],
&ninepix[4],
&ninepix[5],
&ninepix[6],
&ninepix[7],
&ninepix[8],
&row_prev[x_offset - input->elem_stride],
&row_prev[x_offset],
&row_prev[x_offset + input->elem_stride],
&row_curr[x_offset - input->elem_stride],
&row_curr[x_offset],
&row_curr[x_offset + input->elem_stride],
&row_next[x_offset - input->elem_stride],
&row_next[x_offset],
&row_next[x_offset + input->elem_stride]))
{
/* Some rounding magic to make weighting correct with the
* original coefficients. */
uchar result = ((3 * ninepix[0] + 5 * ninepix[1] + 3 * ninepix[2] + 5 * ninepix[3] +
6 * ninepix[4] + 5 * ninepix[5] + 3 * ninepix[6] + 5 * ninepix[7] +
3 * ninepix[8]) *
255.0f +
19.0f) /
38.0f;
out[0] = result / 255.0f;
}
else {
out[0] = row_curr[x_offset];
}
}
}
}
} // namespace blender::compositor

View File

@ -1,54 +0,0 @@
/* SPDX-FileCopyrightText: 2011 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "COM_MultiThreadedOperation.h"
#include "DNA_node_types.h"
namespace blender::compositor {
/**
* \brief AntiAlias operations
* it only supports anti aliasing on BW buffers.
* \ingroup operation
*/
class AntiAliasOperation : public MultiThreadedOperation {
protected:
/**
* \brief Cached reference to the reader
*/
SocketReader *value_reader_;
public:
AntiAliasOperation();
/**
* The inner loop of this operation.
*/
void execute_pixel(float output[4], int x, int y, void *data) override;
/**
* Initialize the execution
*/
void init_execution() override;
void *initialize_tile_data(rcti *rect) override;
/**
* Deinitialize the execution
*/
void deinit_execution() override;
bool determine_depending_area_of_interest(rcti *input,
ReadBufferOperation *read_operation,
rcti *output) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -84,7 +84,7 @@ void MaskOperation::deinit_execution()
void MaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (mask_width_ == 0 || mask_height_ == 0) {
if (!mask_ || mask_width_ == 0 || mask_height_ == 0) {
r_area = COM_AREA_NONE;
}
else {

View File

@ -927,28 +927,10 @@ void MixLightenOperation::execute_pixel_sampled(float output[4],
if (this->use_value_alpha_multiply()) {
value *= input_color2[3];
}
float tmp;
tmp = value * input_color2[0];
if (tmp > input_color1[0]) {
output[0] = tmp;
}
else {
output[0] = input_color1[0];
}
tmp = value * input_color2[1];
if (tmp > input_color1[1]) {
output[1] = tmp;
}
else {
output[1] = input_color1[1];
}
tmp = value * input_color2[2];
if (tmp > input_color1[2]) {
output[2] = tmp;
}
else {
output[2] = input_color1[2];
}
float valuem = 1.0f - value;
output[0] = max_ff(input_color1[0], input_color2[0]) * value + input_color1[0] * valuem;
output[1] = max_ff(input_color1[1], input_color2[1]) * value + input_color1[1] * valuem;
output[2] = max_ff(input_color1[2], input_color2[2]) * value + input_color1[2] * valuem;
output[3] = input_color1[3];
clamp_if_needed(output);
@ -961,16 +943,10 @@ void MixLightenOperation::update_memory_buffer_row(PixelCursor &p)
if (this->use_value_alpha_multiply()) {
value *= p.color2[3];
}
float tmp = value * p.color2[0];
p.out[0] = std::max(tmp, p.color1[0]);
tmp = value * p.color2[1];
p.out[1] = std::max(tmp, p.color1[1]);
tmp = value * p.color2[2];
p.out[2] = std::max(tmp, p.color1[2]);
float value_m = 1.0f - value;
p.out[0] = max_ff(p.color1[0], p.color2[0]) * value + p.color1[0] * value_m;
p.out[1] = max_ff(p.color1[1], p.color2[1]) * value + p.color1[1] * value_m;
p.out[2] = max_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m;
p.out[3] = p.color1[3];
clamp_if_needed(p.out);

View File

@ -398,6 +398,7 @@ PassMain::Sub *ForwardPipeline::material_transparent_add(const Object *ob,
void ForwardPipeline::render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb)
{
if (!has_transparent_ && !has_opaque_) {
inst_.volume.draw_resolve(view);
return;
}

View File

@ -244,9 +244,9 @@ class ReflectionProbeModule {
}
/**
* Get the resolution of a single cubemap side when rendering probes.
* Get the resolution of a single cube-map side when rendering probes.
*
* The cubemaps are rendered half size of the size of the octahedral texture.
* The cube-maps are rendered half size of the size of the octahedral texture.
*/
int probe_render_extent() const;

View File

@ -1090,6 +1090,7 @@ void ShadowModule::end_sync()
sub.bind_resources(inst_.hiz_buffer.front);
sub.bind_resources(inst_.sampling);
sub.bind_resources(inst_.lights);
sub.bind_resources(inst_.volume.properties);
sub.bind_resources(inst_.volume.result);
sub.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
sub.dispatch(math::divide_ceil(inst_.volume.grid_size(), int3(VOLUME_GROUP_SIZE)));

View File

@ -64,8 +64,7 @@ static void step_object_sync_render(void *instance,
{
Instance &inst = *reinterpret_cast<Instance *>(instance);
const bool is_velocity_type = ELEM(
ob->type, OB_CURVES, OB_GPENCIL_LEGACY, OB_MESH, OB_POINTCLOUD);
const bool is_velocity_type = ELEM(ob->type, OB_CURVES, OB_MESH, OB_POINTCLOUD);
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
(ob->type == OB_MESH);
@ -244,6 +243,9 @@ void VelocityModule::geometry_steps_fill()
{
uint dst_ofs = 0;
for (VelocityGeometryData &geom : geometry_map.values()) {
if (!geom.pos_buf) {
continue;
}
uint src_len = GPU_vertbuf_get_vertex_len(geom.pos_buf);
geom.len = src_len;
geom.ofs = dst_ofs;
@ -260,6 +262,9 @@ void VelocityModule::geometry_steps_fill()
copy_ps.bind_ssbo("out_buf", *geometry_steps[step_]);
for (VelocityGeometryData &geom : geometry_map.values()) {
if (!geom.pos_buf) {
continue;
}
const GPUVertFormat *format = GPU_vertbuf_get_format(geom.pos_buf);
if (format->stride == 16) {
GPU_storagebuf_copy_sub_from_vertbuf(*geometry_steps[step_],

View File

@ -36,9 +36,9 @@ class VelocityModule {
/** VertBuf not yet ready to be copied to the #VelocityGeometryBuf. */
GPUVertBuf *pos_buf = nullptr;
/* Offset in the #VelocityGeometryBuf to the start of the data. In vertex. */
int ofs;
int ofs = 0;
/* Length of the vertex buffer. In vertex. */
int len;
int len = 0;
};
/**
* The map contains indirection indices to the obmat and geometry in each step buffer.

View File

@ -616,7 +616,6 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
&texture_buffer,
transform_mode,
IMB_FILTER_NEAREST,
1,
uv_to_texel.ptr(),
crop_rect_ptr);
}

View File

@ -208,6 +208,15 @@ static void grease_pencil_edit_batch_ensure(Object &object,
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
grease_pencil.runtime->batch_cache);
if (cache->edit_points_pos != nullptr) {
return;
}
/* Should be discarded together. */
BLI_assert(cache->edit_points_pos == nullptr && cache->edit_line_indices == nullptr &&
cache->edit_points_indices == nullptr);
BLI_assert(cache->edit_points == nullptr && cache->edit_lines == nullptr);
/* Get the visible drawings. */
const Array<ed::greasepencil::DrawingInfo> drawings =
ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil);
@ -225,8 +234,10 @@ static void grease_pencil_edit_batch_ensure(Object &object,
&format_edit_points_selection, "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
cache->edit_points_pos = GPU_vertbuf_create_with_format(&format_edit_points_pos);
cache->edit_points_selection = GPU_vertbuf_create_with_format(&format_edit_points_selection);
GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
cache->edit_points_pos = GPU_vertbuf_create_with_format_ex(&format_edit_points_pos, vbo_flag);
cache->edit_points_selection = GPU_vertbuf_create_with_format_ex(&format_edit_points_selection,
vbo_flag);
int total_points_num = 0;
for (const ed::greasepencil::DrawingInfo &info : drawings) {
@ -371,6 +382,9 @@ static void grease_pencil_edit_batch_ensure(Object &object,
cache->edit_lines = GPU_batch_create(
GPU_PRIM_LINE_STRIP, cache->edit_points_pos, cache->edit_line_indices);
GPU_batch_vertbuf_add(cache->edit_lines, cache->edit_points_selection, false);
/* Allow creation of buffer texture. */
GPU_vertbuf_use(cache->edit_points_pos);
GPU_vertbuf_use(cache->edit_points_selection);
cache->is_dirty = false;
}

View File

@ -19,7 +19,7 @@ typedef struct GlobalsUboStorage GlobalsUboStorage;
#define UBO_FIRST_COLOR color_wire
#define UBO_LAST_COLOR color_uv_shadow
/* Used as ubo but colors can be directly referenced as well */
/* Used as UBO but colors can be directly referenced as well */
/* \note Also keep all color as vec4 and between #UBO_FIRST_COLOR and #UBO_LAST_COLOR. */
struct GlobalsUboStorage {
/* UBOs data needs to be 16 byte aligned (size of vec4) */

View File

@ -5767,7 +5767,7 @@ void ANIM_channel_draw_widgets(const bContext *C,
UI_UNIT_X,
nullptr);
opptr_b = UI_but_operator_ptr_get(but);
opptr_b = UI_but_operator_ptr_ensure(but);
RNA_int_set(opptr_b, "track_index", channel_index);
UI_block_emboss_set(block, UI_EMBOSS_NONE);

View File

@ -929,6 +929,10 @@ static int add_or_move_to_collection_exec(bContext *C,
bArmature *arm = static_cast<bArmature *>(ob->data);
BoneCollection *target_bcoll = add_or_move_to_collection_bcoll(op, arm);
if (!target_bcoll) {
/* add_or_move_to_collection_bcoll() already reported the reason. */
return OPERATOR_CANCELLED;
}
bool made_any_changes = false;
bool had_bones_to_assign = false;
@ -1107,7 +1111,9 @@ static void move_to_collection_menu_create(bContext *C, uiLayout *layout, void *
child_count = arm->collection_root_count;
}
else {
/* Add a menu item to assign to the parent first, before listing the children. */
/* Add a menu item to assign to the parent first, before listing the children.
* The parent is assumed to be editable, because otherwise the menu would
* have been disabled already one recursion level higher. */
const BoneCollection *parent = arm->collection_array[parent_bcoll_index];
menu_add_item_for_move_assign_unassign(
layout, arm, parent, parent_bcoll_index, is_move_operation);
@ -1122,6 +1128,16 @@ static void move_to_collection_menu_create(bContext *C, uiLayout *layout, void *
for (int index = child_index; index < child_index + child_count; index++) {
const BoneCollection *bcoll = arm->collection_array[index];
/* Avoid assigning/moving to a linked bone collection. */
if (!ANIM_armature_bonecoll_is_editable(arm, bcoll)) {
uiLayout *sub = uiLayoutRow(layout, false);
uiLayoutSetEnabled(sub, false);
/* TODO: figure out if we can add a 'disabled' message in the tooltip. */
menu_add_item_for_move_assign_unassign(sub, arm, bcoll, index, is_move_operation);
continue;
}
if (blender::animrig::bonecoll_has_children(bcoll)) {
uiItemMenuF(layout,
bcoll->name,

View File

@ -1402,7 +1402,7 @@ uiBut *uiDefIconTextButO_ptr(uiBlock *block,
const char *tip);
/** For passing inputs to ButO buttons. */
PointerRNA *UI_but_operator_ptr_get(uiBut *but);
PointerRNA *UI_but_operator_ptr_ensure(uiBut *but);
void UI_but_context_ptr_set(uiBlock *block, uiBut *but, const char *name, const PointerRNA *ptr);
const PointerRNA *UI_but_context_ptr_get(const uiBut *but,
@ -3043,10 +3043,15 @@ void uiItemTabsEnumR_prop(uiLayout *layout,
const char *UI_layout_introspect(uiLayout *layout);
/**
* Helper to add a big icon and create a split layout for alert popups.
* Helpers to add a big icon and create a split layout for alert popups.
* Returns the layout to place further items into the alert box.
*/
uiLayout *uiItemsAlertBox(uiBlock *block, int size, eAlertIcon icon);
uiLayout *uiItemsAlertBox(uiBlock *block,
const uiStyle *style,
const int dialog_width,
const eAlertIcon icon,
const int icon_size);
uiLayout *uiItemsAlertBox(uiBlock *block, const int size, const eAlertIcon icon);
/* UI Operators */
struct uiDragColorHandle {

View File

@ -45,6 +45,7 @@ struct IconTextOverlay {
#define PREVIEW_DEFAULT_HEIGHT 128
enum eAlertIcon {
ALERT_ICON_NONE = -1,
ALERT_ICON_WARNING = 0,
ALERT_ICON_QUESTION = 1,
ALERT_ICON_ERROR = 2,

View File

@ -1359,6 +1359,7 @@ static bool ui_but_event_property_operator_string(const bContext *C,
char *buf,
const size_t buf_maxncpy)
{
using namespace blender;
/* Context toggle operator names to check. */
/* NOTE(@ideasman42): This function could use a refactor to generalize button type to operator
@ -1448,18 +1449,17 @@ static bool ui_but_event_property_operator_string(const bContext *C,
/* There may be multiple data-paths to the same properties,
* support different variations so key bindings are properly detected no matter which are used.
*/
char *data_path_variations[2] = {nullptr};
int data_path_variations_num = 0;
Vector<std::string, 2> data_path_variations;
{
char *data_path = WM_context_path_resolve_property_full(C, ptr, prop, prop_index);
std::string data_path = WM_context_path_resolve_property_full(C, ptr, prop, prop_index);
/* Always iterate once, even if data-path isn't set. */
data_path_variations[data_path_variations_num++] = data_path;
data_path_variations.append(data_path);
if (data_path) {
if (STRPREFIX(data_path, "scene.tool_settings.")) {
data_path_variations[data_path_variations_num++] = BLI_strdup(data_path + 6);
if (!data_path.empty()) {
if (StringRef(data_path).startswith("scene.tool_settings.")) {
data_path_variations.append(StringRef(data_path).drop_known_prefix("scene."));
}
}
}
@ -1467,18 +1467,18 @@ static bool ui_but_event_property_operator_string(const bContext *C,
/* We have a data-path! */
bool found = false;
for (int data_path_index = 0; data_path_index < data_path_variations_num && (found == false);
for (int data_path_index = 0; data_path_index < data_path_variations.size() && (found == false);
data_path_index++)
{
const char *data_path = data_path_variations[data_path_index];
if (data_path || (prop_enum_value_ok && prop_enum_value_id)) {
const StringRefNull data_path = data_path_variations[data_path_index];
if (!data_path.is_empty() || (prop_enum_value_ok && prop_enum_value_id)) {
/* Create a property to host the "data_path" property we're sending to the operators. */
IDProperty *prop_path;
const IDPropertyTemplate group_val = {0};
prop_path = IDP_New(IDP_GROUP, &group_val, __func__);
if (data_path) {
IDP_AddToGroup(prop_path, IDP_NewString(data_path, "data_path"));
if (!data_path.is_empty()) {
IDP_AddToGroup(prop_path, IDP_NewString(data_path.c_str(), "data_path"));
}
if (prop_enum_value_ok) {
const EnumPropertyItem *item;
@ -1522,12 +1522,6 @@ static bool ui_but_event_property_operator_string(const bContext *C,
}
}
for (int data_path_index = 0; data_path_index < data_path_variations_num; data_path_index++) {
char *data_path = data_path_variations[data_path_index];
if (data_path) {
MEM_freeN(data_path);
}
}
return found;
}
@ -5936,7 +5930,7 @@ int UI_but_return_value_get(uiBut *but)
return but->retval;
}
PointerRNA *UI_but_operator_ptr_get(uiBut *but)
PointerRNA *UI_but_operator_ptr_ensure(uiBut *but)
{
if (but->optype && !but->opptr) {
but->opptr = MEM_cnew<PointerRNA>(__func__);
@ -6421,7 +6415,7 @@ static void operator_enum_search_update_fn(
}
else {
/* Will create it if needed! */
PointerRNA *ptr = UI_but_operator_ptr_get(static_cast<uiBut *>(but));
PointerRNA *ptr = UI_but_operator_ptr_ensure(static_cast<uiBut *>(but));
bool do_free;
const EnumPropertyItem *all_items;
@ -6453,7 +6447,7 @@ static void operator_enum_search_exec_fn(bContext * /*C*/, void *but, void *arg2
{
wmOperatorType *ot = ((uiBut *)but)->optype;
/* Will create it if needed! */
PointerRNA *opptr = UI_but_operator_ptr_get(static_cast<uiBut *>(but));
PointerRNA *opptr = UI_but_operator_ptr_ensure(static_cast<uiBut *>(but));
if (ot) {
if (ot->prop) {
@ -6498,7 +6492,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
but->opcontext = WM_OP_EXEC_DEFAULT;
if (properties) {
PointerRNA *ptr = UI_but_operator_ptr_get(but);
PointerRNA *ptr = UI_but_operator_ptr_ensure(but);
/* Copy id-properties. */
ptr->data = IDP_CopyProperty(properties);
}
@ -6577,7 +6571,7 @@ std::optional<EnumPropertyItem> UI_but_rna_enum_item_get(bContext &C, uiBut &but
wmOperatorType *ot = but.optype;
/* So the context is passed to `itemf` functions. */
PointerRNA *opptr = UI_but_operator_ptr_get(&but);
PointerRNA *opptr = UI_but_operator_ptr_ensure(&but);
WM_operator_properties_sanitize(opptr, false);
/* If the default property of the operator is an enum and is set, fetch the tooltip of the
@ -6662,7 +6656,7 @@ std::string UI_but_string_get_rna_label(uiBut &but)
return RNA_property_ui_name(but.rnaprop);
}
if (but.optype) {
PointerRNA *opptr = UI_but_operator_ptr_get(&but);
PointerRNA *opptr = UI_but_operator_ptr_ensure(&but);
return WM_operatortype_name(but.optype, opptr).c_str();
}
if (ELEM(but.type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) {
@ -6717,7 +6711,7 @@ std::string UI_but_string_get_rna_tooltip(bContext &C, uiBut &but)
}
}
else if (but.optype) {
PointerRNA *opptr = UI_but_operator_ptr_get(&but);
PointerRNA *opptr = UI_but_operator_ptr_ensure(&but);
const bContextStore *previous_ctx = CTX_store_get(&C);
CTX_store_set(&C, but.context);
std::string tmp = WM_operatortype_description(&C, but.optype, opptr).c_str();

View File

@ -65,12 +65,7 @@ static FCurve *ui_but_get_fcurve(
void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
{
AnimData *adt;
bAction *act;
FCurve *fcu;
bool driven;
bool special;
/* Clear the flags that this function might set. */
but->flag &= ~(UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN);
but->drawflag &= ~UI_BUT_ANIMATED_CHANGED;
@ -78,41 +73,46 @@ void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
* itself (which are used to animate properties of the animation data).
* We count those as "animated" too for now
*/
fcu = ui_but_get_fcurve(but, &adt, &act, &driven, &special);
AnimData *adt;
bAction *act;
bool driven;
bool special;
FCurve *fcu = ui_but_get_fcurve(but, &adt, &act, &driven, &special);
if (fcu) {
if (!driven) {
/* Empty curves are ignored by the animation evaluation system. */
if (BKE_fcurve_is_empty(fcu)) {
return;
}
if (!fcu) {
return;
}
if (driven) {
but->flag |= UI_BUT_DRIVEN;
return;
}
but->flag |= UI_BUT_ANIMATED;
/* Empty curves are ignored by the animation evaluation system. */
if (BKE_fcurve_is_empty(fcu)) {
return;
}
/* #41525 - When the active action is a NLA strip being edited,
* we need to correct the frame number to "look inside" the
* remapped action
*/
float cfra = anim_eval_context->eval_time;
if (adt) {
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
}
but->flag |= UI_BUT_ANIMATED;
if (fcurve_frame_has_keyframe(fcu, cfra)) {
but->flag |= UI_BUT_ANIMATED_KEY;
}
/* #41525 - When the active action is a NLA strip being edited,
* we need to correct the frame number to "look inside" the
* remapped action
*/
float cfra = anim_eval_context->eval_time;
if (adt) {
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
}
/* XXX: this feature is totally broken and useless with NLA */
if (adt == nullptr || adt->nla_tracks.first == nullptr) {
const AnimationEvalContext remapped_context = BKE_animsys_eval_context_construct_at(
anim_eval_context, cfra);
if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) {
but->drawflag |= UI_BUT_ANIMATED_CHANGED;
}
}
}
else {
but->flag |= UI_BUT_DRIVEN;
if (fcurve_frame_has_keyframe(fcu, cfra)) {
but->flag |= UI_BUT_ANIMATED_KEY;
}
/* XXX: this feature is totally broken and useless with NLA */
if (adt == nullptr || adt->nla_tracks.first == nullptr) {
const AnimationEvalContext remapped_context = BKE_animsys_eval_context_construct_at(
anim_eval_context, cfra);
if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) {
but->drawflag |= UI_BUT_ANIMATED_CHANGED;
}
}
}

View File

@ -61,18 +61,16 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but)
/* If this returns null, we won't be able to bind shortcuts to these RNA properties.
* Support can be added at #wm_context_member_from_ptr. */
char *final_data_path = WM_context_path_resolve_property_full(
std::string final_data_path = WM_context_path_resolve_property_full(
C, &but->rnapoin, but->rnaprop, but->rnaindex);
if (final_data_path == nullptr) {
if (final_data_path.empty()) {
return nullptr;
}
/* Create ID property of data path, to pass to the operator. */
const IDPropertyTemplate val = {0};
IDProperty *prop = IDP_New(IDP_GROUP, &val, __func__);
IDP_AddToGroup(prop, IDP_NewString(final_data_path, "data_path"));
MEM_freeN((void *)final_data_path);
IDP_AddToGroup(prop, IDP_NewString(final_data_path.c_str(), "data_path"));
return prop;
}
@ -321,9 +319,8 @@ static bool ui_but_is_user_menu_compatible(bContext *C, uiBut *but)
}
else if (but->rnaprop) {
if (RNA_property_type(but->rnaprop) == PROP_BOOLEAN) {
char *data_path = WM_context_path_resolve_full(C, &but->rnapoin);
if (data_path != nullptr) {
MEM_freeN(data_path);
std::string data_path = WM_context_path_resolve_full(C, &but->rnapoin);
if (!data_path.empty()) {
result = true;
}
}
@ -346,14 +343,13 @@ static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu *
&um->items, but->optype, prop, "", but->opcontext);
}
if (but->rnaprop) {
char *member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin);
std::string member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin);
/* Ignore the actual array index [pass -1] since the index is handled separately. */
const char *prop_id = RNA_property_is_idprop(but->rnaprop) ?
RNA_path_property_py(&but->rnapoin, but->rnaprop, -1) :
RNA_property_identifier(but->rnaprop);
bUserMenuItem *umi = (bUserMenuItem *)ED_screen_user_menu_item_find_prop(
&um->items, member_id_data_path, prop_id, but->rnaindex);
MEM_freeN(member_id_data_path);
&um->items, member_id_data_path.c_str(), prop_id, but->rnaindex);
return umi;
}
@ -423,14 +419,14 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
}
else if (but->rnaprop) {
/* NOTE: 'member_id' may be a path. */
char *member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin);
std::string member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin);
/* Ignore the actual array index [pass -1] since the index is handled separately. */
const char *prop_id = RNA_property_is_idprop(but->rnaprop) ?
RNA_path_property_py(&but->rnapoin, but->rnaprop, -1) :
RNA_property_identifier(but->rnaprop);
/* NOTE: ignore 'drawstr', use property idname always. */
ED_screen_user_menu_item_add_prop(&um->items, "", member_id_data_path, prop_id, but->rnaindex);
MEM_freeN(member_id_data_path);
ED_screen_user_menu_item_add_prop(
&um->items, "", member_id_data_path.c_str(), prop_id, but->rnaindex);
}
else if ((mt = UI_but_menutype_get(but))) {
ED_screen_user_menu_item_add_menu(&um->items, drawstr.c_str(), mt);

View File

@ -1001,13 +1001,12 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but)
}
/* make a little report about what we've done! */
char *buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex);
if (buf) {
BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf);
MEM_freeN(buf);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, nullptr);
const std::string str = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex);
if (str.empty()) {
return;
}
BKE_report(CTX_wm_reports(C), RPT_PROPERTY, str.c_str());
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, nullptr);
}
static void ui_apply_but_funcs_after(bContext *C)
@ -2734,12 +2733,10 @@ static void ui_but_paste_CurveProfile(bContext *C, uiBut *but)
static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_maxncpy)
{
PointerRNA *opptr = UI_but_operator_ptr_get(but);
PointerRNA *opptr = UI_but_operator_ptr_ensure(but);
char *str;
str = WM_operator_pystring_ex(C, nullptr, false, true, but->optype, opptr);
BLI_strncpy(output, str, output_maxncpy);
MEM_freeN(str);
std::string str = WM_operator_pystring_ex(C, nullptr, false, true, but->optype, opptr);
BLI_strncpy(output, str.c_str(), output_maxncpy);
}
static bool ui_but_copy_menu(uiBut *but, char *output, int output_maxncpy)

View File

@ -1286,7 +1286,7 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout,
/* assign properties */
if (properties || r_opptr) {
PointerRNA *opptr = UI_but_operator_ptr_get(but);
PointerRNA *opptr = UI_but_operator_ptr_ensure(but);
if (properties) {
opptr->data = properties;
}
@ -6287,25 +6287,23 @@ static void ui_layout_introspect_button(DynStr *ds, uiButtonItem *bitem)
BLI_dynstr_appendf(ds, "'tip':'''%s''', ", but->tip ? but->tip : "");
if (but->optype) {
char *opstr = WM_operator_pystring_ex(static_cast<bContext *>(but->block->evil_C),
nullptr,
false,
true,
but->optype,
but->opptr);
BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr ? opstr : "");
MEM_freeN(opstr);
std::string opstr = WM_operator_pystring_ex(static_cast<bContext *>(but->block->evil_C),
nullptr,
false,
true,
but->optype,
but->opptr);
BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr.c_str());
}
{
PropertyRNA *prop = nullptr;
wmOperatorType *ot = UI_but_operatortype_get_from_enum_menu(but, &prop);
if (ot) {
char *opstr = WM_operator_pystring_ex(
std::string opstr = WM_operator_pystring_ex(
static_cast<bContext *>(but->block->evil_C), nullptr, false, true, ot, nullptr);
BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr ? opstr : "");
BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr.c_str());
BLI_dynstr_appendf(ds, "'property':'''%s''', ", prop ? RNA_property_identifier(prop) : "");
MEM_freeN(opstr);
}
}
@ -6396,12 +6394,12 @@ const char *UI_layout_introspect(uiLayout *layout)
/** \name Alert Box with Big Icon
* \{ */
uiLayout *uiItemsAlertBox(uiBlock *block, const int size, const eAlertIcon icon)
uiLayout *uiItemsAlertBox(uiBlock *block,
const uiStyle *style,
const int dialog_width,
const eAlertIcon icon,
const int icon_size)
{
const uiStyle *style = UI_style_get_dpi();
const short icon_size = 64 * UI_SCALE_FAC;
const int text_points_max = std::max(style->widget.points, style->widgetlabel.points);
const int dialog_width = icon_size + (text_points_max * size * UI_SCALE_FAC);
/* By default, the space between icon and text/buttons will be equal to the 'columnspace',
* this extra padding will add some space by increasing the left column width,
* making the icon placement more symmetrical, between the block edge and the text. */
@ -6428,4 +6426,13 @@ uiLayout *uiItemsAlertBox(uiBlock *block, const int size, const eAlertIcon icon)
return layout;
}
uiLayout *uiItemsAlertBox(uiBlock *block, const int size, const eAlertIcon icon)
{
const uiStyle *style = UI_style_get_dpi();
const short icon_size = 64 * UI_SCALE_FAC;
const int text_points_max = std::max(style->widget.points, style->widgetlabel.points);
const int dialog_width = icon_size + (text_points_max * size * UI_SCALE_FAC);
return uiItemsAlertBox(block, style, dialog_width, icon, icon_size);
}
/** \} */

View File

@ -283,15 +283,12 @@ static int copy_python_command_button_exec(bContext *C, wmOperator * /*op*/)
uiBut *but = UI_context_active_but_get(C);
if (but && (but->optype != nullptr)) {
PointerRNA *opptr;
char *str;
opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */
/* allocated when needed, the button owns it */
PointerRNA *opptr = UI_but_operator_ptr_ensure(but);
str = WM_operator_pystring_ex(C, nullptr, false, true, but->optype, opptr);
std::string str = WM_operator_pystring_ex(C, nullptr, false, true, but->optype, opptr);
WM_clipboard_text_set(str, false);
MEM_freeN(str);
WM_clipboard_text_set(str.c_str(), false);
return OPERATOR_FINISHED;
}

View File

@ -347,14 +347,14 @@ static void ui_tooltip_region_free_cb(ARegion *region)
/** \name ToolTip Creation Utility Functions
* \{ */
static char *ui_tooltip_text_python_from_op(bContext *C, wmOperatorType *ot, PointerRNA *opptr)
static std::string ui_tooltip_text_python_from_op(bContext *C,
wmOperatorType *ot,
PointerRNA *opptr)
{
char *str = WM_operator_pystring_ex(C, nullptr, false, false, ot, opptr);
std::string str = WM_operator_pystring_ex(C, nullptr, false, false, ot, opptr);
/* Avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary. */
WM_operator_pystring_abbreviate(str, 32);
return str;
return WM_operator_pystring_abbreviate(std::move(str), 32);
}
/** \} */
@ -397,10 +397,9 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data,
/* Python. */
if (U.flag & USER_TOOLTIPS_PYTHON) {
char *str = ui_tooltip_text_python_from_op(C, ot, kmi->ptr);
std::string str = ui_tooltip_text_python_from_op(C, ot, kmi->ptr);
UI_tooltip_text_field_add(
data, fmt::format(TIP_("Python: {}"), str), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_PYTHON);
MEM_freeN(str);
}
}
}
@ -744,14 +743,13 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* Python */
if ((is_label == false) && (U.flag & USER_TOOLTIPS_PYTHON)) {
char *str = ui_tooltip_text_python_from_op(C, but->optype, but->opptr);
std::string str = ui_tooltip_text_python_from_op(C, but->optype, but->opptr);
UI_tooltip_text_field_add(data,
fmt::format(TIP_("Python: {}"), str),
{},
UI_TIP_STYLE_NORMAL,
UI_TIP_LC_PYTHON,
true);
MEM_freeN(str);
}
/* Keymap */
@ -847,7 +845,8 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
but_tip_label = UI_but_string_get_tooltip_label(*but);
but_tip = UI_but_string_get_tooltip(*C, *but);
enum_label = enum_item ? enum_item->name : "";
enum_tip = enum_item ? enum_item->description : "";
const char *description_c = enum_item ? enum_item->description : nullptr;
enum_tip = description_c ? description_c : "";
if (!is_menu) {
op_keymap = UI_but_string_get_operator_keymap(*C, *but);
prop_keymap = UI_but_string_get_property_keymap(*C, *but);
@ -979,12 +978,12 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
else if (optype) {
PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) :
/* Allocated when needed, the button owns it. */
UI_but_operator_ptr_get(but);
UI_but_operator_ptr_ensure(but);
/* So the context is passed to field functions (some Python field functions use it). */
WM_operator_properties_sanitize(opptr, false);
char *str = ui_tooltip_text_python_from_op(C, optype, opptr);
std::string str = ui_tooltip_text_python_from_op(C, optype, opptr);
/* Operator info. */
if (U.flag & USER_TOOLTIPS_PYTHON) {
@ -995,8 +994,6 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
UI_TIP_LC_PYTHON,
true);
}
MEM_freeN(str);
}
/* Button is disabled, we may be able to tell user why. */

View File

@ -1534,7 +1534,7 @@ static void template_ID(const bContext *C,
UI_UNIT_X,
UI_UNIT_Y,
TIP_("Packed File, click to unpack"));
UI_but_operator_ptr_get(but);
UI_but_operator_ptr_ensure(but);
RNA_string_set(but->opptr, "id_name", id->name + 2);
RNA_int_set(but->opptr, "id_type", GS(id->name));
@ -5911,7 +5911,7 @@ void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname,
UI_UNIT_X,
UI_UNIT_Y,
nullptr);
UI_but_operator_ptr_get(but);
UI_but_operator_ptr_ensure(but);
RNA_enum_set(but->opptr, "type", -1);
but = uiDefIconButO(block,
@ -5924,7 +5924,7 @@ void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname,
UI_UNIT_X,
UI_UNIT_Y,
nullptr);
UI_but_operator_ptr_get(but);
UI_but_operator_ptr_ensure(but);
RNA_enum_set(but->opptr, "type", 1);
/* Menu. */

View File

@ -37,6 +37,7 @@ set(SRC
io_ply_ops.cc
io_stl_ops.cc
io_usd.cc
io_utils.cc
io_alembic.hh
io_cache.hh
@ -48,6 +49,7 @@ set(SRC
io_ply_ops.hh
io_stl_ops.hh
io_usd.hh
io_utils.hh
)
set(LIB

View File

@ -26,6 +26,7 @@
# include "DNA_space_types.h"
# include "BKE_context.hh"
# include "BKE_file_handler.hh"
# include "BKE_main.hh"
# include "BKE_report.h"
@ -52,6 +53,7 @@
# include "DEG_depsgraph.hh"
# include "io_alembic.hh"
# include "io_utils.hh"
# include "ABC_alembic.h"
@ -595,7 +597,7 @@ static int wm_alembic_import_invoke(bContext *C, wmOperator *op, const wmEvent *
if (!RNA_struct_property_is_set(op->ptr, "as_background_job")) {
RNA_boolean_set(op->ptr, "as_background_job", true);
}
return WM_operator_filesel(C, op, event);
return blender::ed::io::filesel_drop_import_invoke(C, op, event);
}
static int wm_alembic_import_exec(bContext *C, wmOperator *op)
@ -651,7 +653,7 @@ void WM_OT_alembic_import(wmOperatorType *ot)
ot->name = "Import Alembic";
ot->description = "Load an Alembic archive";
ot->idname = "WM_OT_alembic_import";
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET;
ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
ot->invoke = wm_alembic_import_invoke;
ot->exec = wm_alembic_import_exec;
@ -716,4 +718,17 @@ void WM_OT_alembic_import(wmOperatorType *ot)
"to run as a background job");
}
namespace blender::ed::io {
void alembic_file_handler_add()
{
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_alembic");
STRNCPY(fh->import_operator, "WM_OT_alembic_import");
STRNCPY(fh->label, "Alembic");
STRNCPY(fh->file_extensions_str, ".abc");
fh->poll_drop = poll_file_object_drop;
bke::file_handler_add(std::move(fh));
}
} // namespace blender::ed::io
#endif

View File

@ -12,3 +12,7 @@ struct wmOperatorType;
void WM_OT_alembic_export(wmOperatorType *ot);
void WM_OT_alembic_import(wmOperatorType *ot);
namespace blender::ed::io {
void alembic_file_handler_add();
}

View File

@ -11,9 +11,11 @@
# include "BLT_translation.h"
# include "BLI_blenlib.h"
# include "BLI_string.h"
# include "BLI_utildefines.h"
# include "BKE_context.hh"
# include "BKE_file_handler.hh"
# include "BKE_main.hh"
# include "BKE_object.hh"
# include "BKE_report.h"
@ -22,6 +24,7 @@
# include "ED_fileselect.hh"
# include "ED_object.hh"
# include "ED_outliner.hh"
# include "RNA_access.hh"
# include "RNA_define.hh"
@ -35,6 +38,7 @@
# include "collada.h"
# include "io_collada.hh"
# include "io_utils.hh"
static int wm_collada_export_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
@ -750,6 +754,11 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op)
if (collada_import(C, &import_settings)) {
DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS);
Scene *scene = CTX_data_scene(C);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
}
@ -757,7 +766,7 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr)
static void wm_collada_import_settings(uiLayout *layout, PointerRNA *imfptr)
{
uiLayout *box, *col;
@ -787,7 +796,7 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr)
static void wm_collada_import_draw(bContext * /*C*/, wmOperator *op)
{
uiCollada_importSettings(op->layout, op->ptr);
wm_collada_import_settings(op->layout, op->ptr);
}
void WM_OT_collada_import(wmOperatorType *ot)
@ -795,9 +804,9 @@ void WM_OT_collada_import(wmOperatorType *ot)
ot->name = "Import COLLADA";
ot->description = "Load a Collada file";
ot->idname = "WM_OT_collada_import";
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET;
ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
ot->invoke = WM_operator_filesel;
ot->invoke = blender::ed::io::filesel_drop_import_invoke;
ot->exec = wm_collada_import_exec;
ot->poll = WM_operator_winactive;
@ -862,4 +871,18 @@ void WM_OT_collada_import(wmOperatorType *ot)
"Keep Bind Info",
"Store Bindpose information in custom bone properties for later use during Collada export");
}
namespace blender::ed::io {
void collada_file_handler_add()
{
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_collada");
STRNCPY(fh->import_operator, "WM_OT_collada_import");
STRNCPY(fh->label, "Collada");
STRNCPY(fh->file_extensions_str, ".dae");
fh->poll_drop = poll_file_object_drop;
bke::file_handler_add(std::move(fh));
}
} // namespace blender::ed::io
#endif

View File

@ -12,3 +12,7 @@ struct wmOperatorType;
void WM_OT_collada_export(wmOperatorType *ot);
void WM_OT_collada_import(wmOperatorType *ot);
namespace blender::ed::io {
void collada_file_handler_add();
}

View File

@ -24,3 +24,7 @@ void WM_OT_gpencil_export_pdf(wmOperatorType *ot);
ARegion *get_invoke_region(bContext *C);
View3D *get_invoke_view3d(bContext *C);
namespace blender::ed::io {
void gpencil_file_handler_add();
}

View File

@ -9,6 +9,7 @@
#ifdef WITH_IO_GPENCIL
# include "BLI_path_util.h"
# include "BLI_string.h"
# include "MEM_guardedalloc.h"
@ -16,6 +17,7 @@
# include "DNA_space_types.h"
# include "BKE_context.hh"
# include "BKE_file_handler.hh"
# include "BKE_gpencil_legacy.h"
# include "BKE_report.h"
@ -36,6 +38,7 @@
# include "ED_gpencil_legacy.hh"
# include "io_gpencil.hh"
# include "io_utils.hh"
# include "gpencil_io.h"
@ -55,13 +58,6 @@ static bool wm_gpencil_import_svg_common_check(bContext * /*C*/, wmOperator *op)
return false;
}
static int wm_gpencil_import_svg_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
@ -136,7 +132,8 @@ static void ui_gpencil_import_svg_settings(uiLayout *layout, PointerRNA *imfptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayout *col = uiLayoutColumn(layout, false);
uiLayout *box = uiLayoutBox(layout);
uiLayout *col = uiLayoutColumn(box, false);
uiItemR(col, imfptr, "resolution", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, imfptr, "scale", UI_ITEM_NONE, nullptr, ICON_NONE);
}
@ -161,7 +158,7 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot)
ot->description = "Import SVG into grease pencil";
ot->idname = "WM_OT_gpencil_import_svg";
ot->invoke = wm_gpencil_import_svg_invoke;
ot->invoke = blender::ed::io::filesel_drop_import_invoke;
ot->exec = wm_gpencil_import_svg_exec;
ot->poll = wm_gpencil_import_svg_poll;
ot->ui = wm_gpencil_import_svg_draw;
@ -197,4 +194,17 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot)
100.0f);
}
namespace blender::ed::io {
void gpencil_file_handler_add()
{
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_gpencil_svg");
STRNCPY(fh->import_operator, "WM_OT_gpencil_import_svg");
STRNCPY(fh->label, "SVG as Grease Pencil");
STRNCPY(fh->file_extensions_str, ".svg");
fh->poll_drop = poll_file_object_drop;
bke::file_handler_add(std::move(fh));
}
} // namespace blender::ed::io
#endif /* WITH_IO_GPENCIL */

View File

@ -11,6 +11,7 @@
# include "DNA_space_types.h"
# include "BKE_context.hh"
# include "BKE_file_handler.hh"
# include "BKE_main.hh"
# include "BKE_report.h"
@ -41,6 +42,7 @@
# include "IO_wavefront_obj.hh"
# include "io_obj.hh"
# include "io_utils.hh"
static const EnumPropertyItem io_obj_export_evaluation_mode[] = {
{DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"},
@ -385,12 +387,6 @@ void WM_OT_obj_export(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int wm_obj_import_exec(bContext *C, wmOperator *op)
{
OBJImportParams import_params{};
@ -485,9 +481,9 @@ void WM_OT_obj_import(wmOperatorType *ot)
ot->name = "Import Wavefront OBJ";
ot->description = "Load a Wavefront OBJ scene";
ot->idname = "WM_OT_obj_import";
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET;
ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
ot->invoke = wm_obj_import_invoke;
ot->invoke = blender::ed::io::filesel_drop_import_invoke;
ot->exec = wm_obj_import_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_obj_import_draw;
@ -559,4 +555,17 @@ void WM_OT_obj_import(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
namespace blender::ed::io {
void obj_file_handler_add()
{
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_obj");
STRNCPY(fh->import_operator, "WM_OT_obj_import");
STRNCPY(fh->label, "Wavefront OBJ");
STRNCPY(fh->file_extensions_str, ".obj");
fh->poll_drop = poll_file_object_drop;
bke::file_handler_add(std::move(fh));
}
} // namespace blender::ed::io
#endif /* WITH_IO_WAVEFRONT_OBJ */

View File

@ -12,3 +12,7 @@ struct wmOperatorType;
void WM_OT_obj_export(wmOperatorType *ot);
void WM_OT_obj_import(wmOperatorType *ot);
namespace blender::ed::io {
void obj_file_handler_add();
}

View File

@ -31,22 +31,27 @@
void ED_operatortypes_io()
{
using namespace blender;
#ifdef WITH_COLLADA
/* Collada operators: */
WM_operatortype_append(WM_OT_collada_export);
WM_operatortype_append(WM_OT_collada_import);
ed::io::collada_file_handler_add();
#endif
#ifdef WITH_ALEMBIC
WM_operatortype_append(WM_OT_alembic_import);
WM_operatortype_append(WM_OT_alembic_export);
ed::io::alembic_file_handler_add();
#endif
#ifdef WITH_USD
WM_operatortype_append(WM_OT_usd_import);
WM_operatortype_append(WM_OT_usd_export);
ed::io::usd_file_handler_add();
#endif
#ifdef WITH_IO_GPENCIL
WM_operatortype_append(WM_OT_gpencil_import_svg);
ed::io::gpencil_file_handler_add();
# ifdef WITH_PUGIXML
WM_operatortype_append(WM_OT_gpencil_export_svg);
# endif
@ -64,16 +69,19 @@ void ED_operatortypes_io()
#ifdef WITH_IO_WAVEFRONT_OBJ
WM_operatortype_append(WM_OT_obj_export);
WM_operatortype_append(WM_OT_obj_import);
ed::io::obj_file_handler_add();
#endif
#ifdef WITH_IO_PLY
WM_operatortype_append(WM_OT_ply_export);
WM_operatortype_append(WM_OT_ply_import);
ed::io::ply_file_handler_add();
#endif
#ifdef WITH_IO_STL
WM_operatortype_append(WM_OT_stl_import);
WM_operatortype_append(WM_OT_stl_export);
ed::io::stl_file_handler_add();
#endif
WM_operatortype_append(WM_OT_drop_import_file);
ED_dropbox_drop_import_file();

View File

@ -9,9 +9,12 @@
#ifdef WITH_IO_PLY
# include "BKE_context.hh"
# include "BKE_file_handler.hh"
# include "BKE_main.hh"
# include "BKE_report.h"
# include "BLI_string.h"
# include "WM_api.hh"
# include "WM_types.hh"
@ -37,6 +40,7 @@
# include "IO_ply.hh"
# include "io_ply_ops.hh"
# include "io_utils.hh"
static const EnumPropertyItem ply_vertex_colors_mode[] = {
{PLY_VERTEX_COLOR_NONE, "NONE", 0, "None", "Do not import/export color attributes"},
@ -236,11 +240,6 @@ void WM_OT_ply_export(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
static int wm_ply_import_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return WM_operator_filesel(C, op, event);
}
static int wm_ply_import_exec(bContext *C, wmOperator *op)
{
PLYImportParams params{};
@ -286,6 +285,26 @@ static int wm_ply_import_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static void ui_ply_import_settings(uiLayout *layout, PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayout *box = uiLayoutBox(layout);
uiLayout *col = uiLayoutColumn(box, false);
uiItemR(col, ptr, "global_scale", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "use_scene_unit", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "forward_axis", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "up_axis", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "merge_verts", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "import_colors", UI_ITEM_NONE, nullptr, ICON_NONE);
}
static void wm_ply_import_draw(bContext * /*C*/, wmOperator *op)
{
ui_ply_import_settings(op->layout, op->ptr);
}
void WM_OT_ply_import(wmOperatorType *ot)
{
PropertyRNA *prop;
@ -294,10 +313,11 @@ void WM_OT_ply_import(wmOperatorType *ot)
ot->description = "Import an PLY file as an object";
ot->idname = "WM_OT_ply_import";
ot->invoke = wm_ply_import_invoke;
ot->invoke = blender::ed::io::filesel_drop_import_invoke;
ot->exec = wm_ply_import_exec;
ot->ui = wm_ply_import_draw;
ot->poll = WM_operator_winactive;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET;
ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER,
@ -333,4 +353,17 @@ void WM_OT_ply_import(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
namespace blender::ed::io {
void ply_file_handler_add()
{
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_ply");
STRNCPY(fh->import_operator, "WM_OT_ply_import");
STRNCPY(fh->label, "Stanford PLY");
STRNCPY(fh->file_extensions_str, ".ply");
fh->poll_drop = poll_file_object_drop;
bke::file_handler_add(std::move(fh));
}
} // namespace blender::ed::io
#endif /* WITH_IO_PLY */

View File

@ -12,3 +12,7 @@ struct wmOperatorType;
void WM_OT_ply_export(wmOperatorType *ot);
void WM_OT_ply_import(wmOperatorType *ot);
namespace blender::ed::io {
void ply_file_handler_add();
}

View File

@ -9,8 +9,11 @@
#ifdef WITH_IO_STL
# include "BKE_context.hh"
# include "BKE_file_handler.hh"
# include "BKE_report.h"
# include "BLI_string.h"
# include "WM_api.hh"
# include "WM_types.hh"
@ -29,6 +32,7 @@
# include "IO_stl.hh"
# include "io_stl_ops.hh"
# include "io_utils.hh"
static int wm_stl_export_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
@ -175,11 +179,6 @@ void WM_OT_stl_export(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
static int wm_stl_import_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return WM_operator_filesel(C, op, event);
}
static int wm_stl_import_exec(bContext *C, wmOperator *op)
{
STLImportParams params{};
@ -237,6 +236,26 @@ static bool wm_stl_import_check(bContext * /*C*/, wmOperator *op)
return false;
}
static void ui_stl_import_settings(uiLayout *layout, PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayout *box = uiLayoutBox(layout);
uiLayout *col = uiLayoutColumn(box, false);
uiItemR(col, ptr, "global_scale", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "use_scene_unit", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "use_facet_normal", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "forward_axis", UI_ITEM_NONE, IFACE_("Forward Axis"), ICON_NONE);
uiItemR(col, ptr, "up_axis", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "use_mesh_validate", UI_ITEM_NONE, nullptr, ICON_NONE);
}
static void wm_stl_import_draw(bContext * /*C*/, wmOperator *op)
{
ui_stl_import_settings(op->layout, op->ptr);
}
void WM_OT_stl_import(wmOperatorType *ot)
{
PropertyRNA *prop;
@ -245,11 +264,12 @@ void WM_OT_stl_import(wmOperatorType *ot)
ot->description = "Import an STL file as an object";
ot->idname = "WM_OT_stl_import";
ot->invoke = wm_stl_import_invoke;
ot->invoke = blender::ed::io::filesel_drop_import_invoke;
ot->exec = wm_stl_import_exec;
ot->poll = WM_operator_winactive;
ot->check = wm_stl_import_check;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET;
ot->ui = wm_stl_import_draw;
ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER,
@ -284,4 +304,17 @@ void WM_OT_stl_import(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
namespace blender::ed::io {
void stl_file_handler_add()
{
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_stl");
STRNCPY(fh->import_operator, "WM_OT_stl_import");
STRNCPY(fh->label, "STL");
STRNCPY(fh->file_extensions_str, ".stl");
fh->poll_drop = poll_file_object_drop;
bke::file_handler_add(std::move(fh));
}
} // namespace blender::ed::io
#endif /* WITH_IO_STL */

View File

@ -12,3 +12,7 @@ struct wmOperatorType;
void WM_OT_stl_export(wmOperatorType *ot);
void WM_OT_stl_import(wmOperatorType *ot);
namespace blender::ed::io {
void stl_file_handler_add();
}

View File

@ -13,6 +13,7 @@
# include <cstring>
# include "BKE_context.hh"
# include "BKE_file_handler.hh"
# include "BKE_main.hh"
# include "BKE_report.h"
@ -42,6 +43,7 @@
# include "DEG_depsgraph.hh"
# include "io_usd.hh"
# include "io_utils.hh"
# include "usd.h"
# include <cstdio>
@ -457,7 +459,7 @@ static int wm_usd_import_invoke(bContext *C, wmOperator *op, const wmEvent *even
options->as_background_job = true;
op->customdata = options;
return WM_operator_filesel(C, op, event);
return blender::ed::io::filesel_drop_import_invoke(C, op, event);
}
static int wm_usd_import_exec(bContext *C, wmOperator *op)
@ -603,7 +605,6 @@ static void wm_usd_import_draw(bContext * /*C*/, wmOperator *op)
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayout *box = uiLayoutBox(layout);
uiLayout *col = uiLayoutColumnWithHeading(box, true, IFACE_("Data Types"));
uiItemR(col, ptr, "import_cameras", UI_ITEM_NONE, nullptr, ICON_NONE);
@ -672,7 +673,7 @@ void WM_OT_usd_import(wmOperatorType *ot)
ot->poll = WM_operator_winactive;
ot->ui = wm_usd_import_draw;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET;
ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_USD,
@ -827,4 +828,17 @@ void WM_OT_usd_import(wmOperatorType *ot)
"Behavior when the name of an imported texture file conflicts with an existing file");
}
namespace blender::ed::io {
void usd_file_handler_add()
{
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_usd");
STRNCPY(fh->import_operator, "WM_OT_usd_import");
STRNCPY(fh->label, "Universal Scene Description");
STRNCPY(fh->file_extensions_str, ".usd;.usda;.usdc;.usdz");
fh->poll_drop = poll_file_object_drop;
bke::file_handler_add(std::move(fh));
}
} // namespace blender::ed::io
#endif /* WITH_USD */

View File

@ -12,3 +12,6 @@ struct wmOperatorType;
void WM_OT_usd_export(wmOperatorType *ot);
void WM_OT_usd_import(wmOperatorType *ot);
namespace blender::ed::io {
void usd_file_handler_add();
}

View File

@ -0,0 +1,50 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_context.hh"
#include "DNA_space_types.h"
#include "ED_fileselect.hh"
#include "RNA_access.hh"
#include "WM_api.hh"
#include "io_utils.hh"
namespace blender::ed::io {
int filesel_drop_import_invoke(bContext *C, wmOperator *op, const wmEvent * /* event */)
{
PropertyRNA *filepath_prop = RNA_struct_find_property(op->ptr, "filepath");
PropertyRNA *directory_prop = RNA_struct_find_property(op->ptr, "directory");
if ((filepath_prop && RNA_property_is_set(op->ptr, filepath_prop)) ||
(directory_prop && RNA_property_is_set(op->ptr, directory_prop)))
{
return WM_operator_props_dialog_popup(C, op, 350);
}
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
bool poll_file_object_drop(const bContext *C, blender::bke::FileHandlerType * /*fh*/)
{
View3D *v3d = CTX_wm_view3d(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
ARegion *region = CTX_wm_region(C);
if (!region || region->regiontype != RGN_TYPE_WINDOW) {
return false;
}
if (v3d) {
return true;
}
if (space_outliner && space_outliner->outlinevis == SO_VIEW_LAYER) {
return true;
}
return false;
}
} // namespace blender::ed::io

View File

@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "WM_types.hh"
struct wmOperator;
struct wmOperatorType;
struct wmDrag;
struct wmDropBox;
namespace blender::bke {
struct FileHanlderType;
} // namespace blender::bke
namespace blender::ed::io {
/**
* Shows a import dialog if the operator was invoked with filepath properties set,
* otherwise invokes the file-select window.
*/
int filesel_drop_import_invoke(bContext *C, wmOperator *op, const wmEvent *event);
bool poll_file_object_drop(const bContext *C, blender::bke::FileHandlerType *fh);
} // namespace blender::ed::io

View File

@ -185,20 +185,34 @@ static bool is_constrained_by_radius(const Brush *br)
return false;
}
/* Fetch the propogation_steps value, preferring the brush level value over the global sculpt tool
* value. */
static int boundary_propagation_steps(const Sculpt *sd, const Brush *brush)
{
return brush && brush->automasking_flags &
(BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) ?
brush->automasking_boundary_edges_propagation_steps :
sd->automasking_boundary_edges_propagation_steps;
}
/* Determine if the given automasking settings require values to be precomputed and cached. */
static bool needs_factors_cache(const Sculpt *sd, const Brush *brush)
{
const int automasking_flags = calc_effective_bits(sd, brush);
if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY && brush && is_constrained_by_radius(brush)) {
return true;
}
if (automasking_flags & (BRUSH_AUTOMASKING_BOUNDARY_EDGES |
BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS | BRUSH_AUTOMASKING_VIEW_NORMAL))
{
if (automasking_flags & BRUSH_AUTOMASKING_VIEW_NORMAL) {
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
}
if (automasking_flags &
(BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS))
{
return boundary_propagation_steps(sd, brush) != 1;
}
return false;
}
@ -917,10 +931,6 @@ std::unique_ptr<Cache> cache_init(Sculpt *sd, Brush *brush, Object *ob)
(*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = initial_value;
}
const int boundary_propagation_steps = brush ?
brush->automasking_boundary_edges_propagation_steps :
1;
/* Additive modes. */
if (mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_ensure(ss);
@ -934,13 +944,14 @@ std::unique_ptr<Cache> cache_init(Sculpt *sd, Brush *brush, Object *ob)
init_face_sets_masking(sd, ob);
}
const int steps = boundary_propagation_steps(sd, brush);
if (mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
SCULPT_vertex_random_access_ensure(ss);
init_boundary_masking(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps);
init_boundary_masking(ob, AUTOMASK_INIT_BOUNDARY_EDGES, steps);
}
if (mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
init_boundary_masking(ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps);
init_boundary_masking(ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, steps);
}
/* Subtractive modes. */

View File

@ -1469,7 +1469,7 @@ static void file_draw_invalid_asset_library_hint(const bContext *C,
UI_UNIT_X * 8,
UI_UNIT_Y,
nullptr);
PointerRNA *but_opptr = UI_but_operator_ptr_get(but);
PointerRNA *but_opptr = UI_but_operator_ptr_ensure(but);
RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS);
UI_block_end(C, block);

View File

@ -1415,7 +1415,7 @@ static void view3d_panel_vgroup(const bContext *C, Panel *panel)
(x = UI_UNIT_X * 5),
UI_UNIT_Y,
"");
but_ptr = UI_but_operator_ptr_get(but);
but_ptr = UI_but_operator_ptr_ensure(but);
RNA_int_set(but_ptr, "weight_group", i);
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
if (BKE_object_defgroup_active_index_get(ob) != i + 1) {

View File

@ -321,7 +321,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
else if (t->spacetype == SPACE_SEQ && region->regiontype == RGN_TYPE_PREVIEW) {
t->options |= CTX_SEQUENCER_IMAGE;
/* Needed for autokeying transforms in preview during playback. */
/* Needed for auto-keying transforms in preview during playback. */
bScreen *animscreen = ED_screen_animation_playing(CTX_wm_manager(C));
t->animtimer = (animscreen) ? animscreen->animtimer : nullptr;
}

View File

@ -88,9 +88,9 @@ class IndexedFaceSet : public Rep {
* iTIndices
* The Texture coordinates indices (per vertex). The integers contained in this array must
* be multiple of 2. iTISize The size of iMIndices iCopy 0 : the arrays are not copied. The
* pointers passed as arguments are used. IndexedFaceSet takes these arrays desallocation in
* pointers passed as arguments are used. IndexedFaceSet takes these arrays deallocation in
* charge. 1 : the arrays are copied. The caller is in charge of the arrays, passed as arguments
* desallocation.
* deallocation.
*/
IndexedFaceSet(float *iVertices,
uint iVSize,

View File

@ -413,6 +413,15 @@ void ShaderCreateInfo::validate_vertex_attributes(const ShaderCreateInfo *other_
using namespace blender::gpu::shader;
#ifdef _MSC_VER
/* Disable optimization for this function with MSVC. It does not like the fact
* shaders info are declared in the same function (same basic block or not does
* not change anything).
* Since it is just a function called to register shaders (once),
* the fact it's optimized or not does not matter, it's not on any hot
* code path. */
# pragma optimize("", off)
#endif
void gpu_shader_create_info_init()
{
g_create_infos = new CreateInfoDictionnary();
@ -553,6 +562,9 @@ void gpu_shader_create_info_init()
/* TEST */
// gpu_shader_create_info_compile(nullptr);
}
#ifdef _MSC_VER
# pragma optimize("", on)
#endif
void gpu_shader_create_info_exit()
{

View File

@ -266,6 +266,7 @@ enum eIMBInterpolationFilterMode {
IMB_FILTER_BILINEAR,
IMB_FILTER_CUBIC_BSPLINE,
IMB_FILTER_CUBIC_MITCHELL,
IMB_FILTER_BOX,
};
/**
@ -651,8 +652,6 @@ enum eIMBTransformMode {
* - Only one data type buffer will be used (rect_float has priority over rect)
* \param mode: Cropping/Wrap repeat effect to apply during transformation.
* \param filter: Interpolation to use during sampling.
* \param num_subsamples: Number of subsamples to use. Increasing this would improve the quality,
* but reduces the performance.
* \param transform_matrix: Transformation matrix to use.
* The given matrix should transform between dst pixel space to src pixel space.
* One unit is one pixel.
@ -667,7 +666,6 @@ void IMB_transform(const ImBuf *src,
ImBuf *dst,
eIMBTransformMode mode,
eIMBInterpolationFilterMode filter,
const int num_subsamples,
const float transform_matrix[4][4],
const rctf *src_crop);

View File

@ -15,7 +15,6 @@
#include "BLI_math_vector.h"
#include "BLI_rect.h"
#include "BLI_task.hh"
#include "BLI_vector.hh"
#include "IMB_imbuf.hh"
#include "IMB_interp.hh"
@ -39,42 +38,21 @@ struct TransformContext {
/* Source UV step delta, when moving along one destination pixel in Y axis. */
float2 add_y;
/* Per-subsample source image delta UVs. */
Vector<float2, 9> subsampling_deltas;
IndexRange dst_region_x_range;
IndexRange dst_region_y_range;
/* Cropping region in source image pixel space. */
rctf src_crop;
void init(const float4x4 &transform_matrix, const int num_subsamples, const bool has_source_crop)
void init(const float4x4 &transform_matrix, const bool has_source_crop)
{
start_uv = transform_matrix.location().xy();
add_x = transform_matrix.x_axis().xy();
add_y = transform_matrix.y_axis().xy();
init_subsampling(num_subsamples);
init_destination_region(transform_matrix, has_source_crop);
}
private:
void init_subsampling(const int num_subsamples)
{
float2 subsample_add_x = add_x / num_subsamples;
float2 subsample_add_y = add_y / num_subsamples;
float2 offset_x = -add_x * 0.5f + subsample_add_x * 0.5f;
float2 offset_y = -add_y * 0.5f + subsample_add_y * 0.5f;
for (int y : IndexRange(0, num_subsamples)) {
for (int x : IndexRange(0, num_subsamples)) {
float2 delta_uv = offset_x + offset_y;
delta_uv += x * subsample_add_x;
delta_uv += y * subsample_add_y;
subsampling_deltas.append(delta_uv);
}
}
}
void init_destination_region(const float4x4 &transform_matrix, const bool has_source_crop)
{
if (!has_source_crop) {
@ -265,27 +243,47 @@ template<eIMBInterpolationFilterMode Filter,
bool WrapUV>
static void process_scanlines(const TransformContext &ctx, IndexRange y_range)
{
/* Note: sample at pixel center for proper filtering. */
float2 uv_start = ctx.start_uv + ctx.add_x * 0.5f + ctx.add_y * 0.5f;
if constexpr (Filter == IMB_FILTER_BOX) {
if (ctx.subsampling_deltas.size() > 1) {
/* Multiple samples per pixel: accumulate them pre-multiplied,
* divide by sample count and write out (un-pre-multiplying if writing out
* to byte image). */
const float inv_count = 1.0f / ctx.subsampling_deltas.size();
* to byte image).
*
* Do a box filter: for each destination pixel, accumulate XxY samples from source,
* based on scaling factors (length of X/Y pixel steps). Use at least 2 samples
* along each direction, so that in case of rotation the resulting edges get
* some anti-aliasing, to match previous Subsampled3x3 filter behavior. The
* "at least 2" can be removed once/if transform edge anti-aliasing is implemented
* in general way for all filters. Use at most 100 samples along each direction,
* just as some way of clamping possible upper cost. Scaling something down by more
* than 100x should rarely if ever happen, worst case they will get some aliasing.
*/
float2 uv_start = ctx.start_uv;
int sub_count_x = int(math::clamp(roundf(math::length(ctx.add_x)), 2.0f, 100.0f));
int sub_count_y = int(math::clamp(roundf(math::length(ctx.add_y)), 2.0f, 100.0f));
const float inv_count = 1.0f / (sub_count_x * sub_count_y);
const float2 sub_step_x = ctx.add_x / sub_count_x;
const float2 sub_step_y = ctx.add_y / sub_count_y;
for (int yi : y_range) {
T *output = init_pixel_pointer<T>(ctx.dst, ctx.dst_region_x_range.first(), yi);
float2 uv_row = uv_start + yi * ctx.add_y;
for (int xi : ctx.dst_region_x_range) {
float2 uv = uv_row + xi * ctx.add_x;
const float2 uv = uv_row + xi * ctx.add_x;
float sample[4] = {};
for (const float2 &delta_uv : ctx.subsampling_deltas) {
const float2 sub_uv = uv + delta_uv;
if (!CropSource || !should_discard(ctx, sub_uv)) {
T sub_sample[4];
sample_image<Filter, T, SrcChannels, WrapUV>(ctx.src, sub_uv.x, sub_uv.y, sub_sample);
add_subsample(sub_sample, sample);
for (int sub_y = 0; sub_y < sub_count_y; sub_y++) {
for (int sub_x = 0; sub_x < sub_count_x; sub_x++) {
float2 delta = (sub_x + 0.5f) * sub_step_x + (sub_y + 0.5f) * sub_step_y;
float2 sub_uv = uv + delta;
if (!CropSource || !should_discard(ctx, sub_uv)) {
T sub_sample[4];
sample_image<eIMBInterpolationFilterMode::IMB_FILTER_NEAREST,
T,
SrcChannels,
WrapUV>(ctx.src, sub_uv.x, sub_uv.y, sub_sample);
add_subsample(sub_sample, sample);
}
}
}
@ -297,7 +295,8 @@ static void process_scanlines(const TransformContext &ctx, IndexRange y_range)
}
}
else {
/* One sample per pixel. */
/* One sample per pixel. Note: sample at pixel center for proper filtering. */
float2 uv_start = ctx.start_uv + ctx.add_x * 0.5f + ctx.add_y * 0.5f;
for (int yi : y_range) {
T *output = init_pixel_pointer<T>(ctx.dst, ctx.dst_region_x_range.first(), yi);
float2 uv_row = uv_start + yi * ctx.add_y;
@ -369,7 +368,6 @@ void IMB_transform(const ImBuf *src,
ImBuf *dst,
const eIMBTransformMode mode,
const eIMBInterpolationFilterMode filter,
const int num_subsamples,
const float transform_matrix[4][4],
const rctf *src_crop)
{
@ -386,7 +384,7 @@ void IMB_transform(const ImBuf *src,
if (crop) {
ctx.src_crop = *src_crop;
}
ctx.init(blender::float4x4(transform_matrix), num_subsamples, crop);
ctx.init(blender::float4x4(transform_matrix), crop);
threading::parallel_for(ctx.dst_region_y_range, 8, [&](IndexRange y_range) {
if (filter == IMB_FILTER_NEAREST) {
@ -401,5 +399,8 @@ void IMB_transform(const ImBuf *src,
else if (filter == IMB_FILTER_CUBIC_MITCHELL) {
transform_scanlines_filter<IMB_FILTER_CUBIC_MITCHELL>(ctx, y_range);
}
else if (filter == IMB_FILTER_BOX) {
transform_scanlines_filter<IMB_FILTER_BOX>(ctx, y_range);
}
});
}

View File

@ -37,29 +37,29 @@ static ImBuf *create_6x2_test_image()
return img;
}
static ImBuf *transform_2x_smaller(eIMBInterpolationFilterMode filter, int subsamples)
static ImBuf *transform_2x_smaller(eIMBInterpolationFilterMode filter)
{
ImBuf *src = create_6x2_test_image();
ImBuf *dst = IMB_allocImBuf(3, 1, 32, IB_rect);
float4x4 matrix = math::from_scale<float4x4>(float4(2.0f));
IMB_transform(src, dst, IMB_TRANSFORM_MODE_REGULAR, filter, subsamples, matrix.ptr(), nullptr);
IMB_transform(src, dst, IMB_TRANSFORM_MODE_REGULAR, filter, matrix.ptr(), nullptr);
IMB_freeImBuf(src);
return dst;
}
static ImBuf *transform_fractional_larger(eIMBInterpolationFilterMode filter, int subsamples)
static ImBuf *transform_fractional_larger(eIMBInterpolationFilterMode filter)
{
ImBuf *src = create_6x2_test_image();
ImBuf *dst = IMB_allocImBuf(9, 7, 32, IB_rect);
float4x4 matrix = math::from_scale<float4x4>(float4(6.0f / 9.0f, 2.0f / 7.0f, 1.0f, 1.0f));
IMB_transform(src, dst, IMB_TRANSFORM_MODE_REGULAR, filter, subsamples, matrix.ptr(), nullptr);
IMB_transform(src, dst, IMB_TRANSFORM_MODE_REGULAR, filter, matrix.ptr(), nullptr);
IMB_freeImBuf(src);
return dst;
}
TEST(imbuf_transform, nearest_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_NEAREST, 1);
ImBuf *res = transform_2x_smaller(IMB_FILTER_NEAREST);
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
EXPECT_EQ(got[0], ColorTheme4b(255, 255, 255, 255));
EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 19));
@ -67,19 +67,20 @@ TEST(imbuf_transform, nearest_2x_smaller)
IMB_freeImBuf(res);
}
TEST(imbuf_transform, nearest_subsample3_2x_smaller)
TEST(imbuf_transform, box_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_NEAREST, 3);
ImBuf *res = transform_2x_smaller(IMB_FILTER_BOX);
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
EXPECT_EQ(got[0], ColorTheme4b(227, 170, 113, 255));
EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 17));
EXPECT_EQ(got[2], ColorTheme4b(56, 22, 64, 253));
/* At 2x reduction should be same as bilinear, save for some rounding errors. */
EXPECT_EQ(got[0], ColorTheme4b(191, 128, 64, 255));
EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 16));
EXPECT_EQ(got[2], ColorTheme4b(54, 50, 48, 254));
IMB_freeImBuf(res);
}
TEST(imbuf_transform, bilinear_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_BILINEAR, 1);
ImBuf *res = transform_2x_smaller(IMB_FILTER_BILINEAR);
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
EXPECT_EQ(got[0], ColorTheme4b(191, 128, 64, 255));
EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 16));
@ -89,7 +90,7 @@ TEST(imbuf_transform, bilinear_2x_smaller)
TEST(imbuf_transform, cubic_bspline_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_BSPLINE, 1);
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_BSPLINE);
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
EXPECT_EQ(got[0], ColorTheme4b(189, 126, 62, 250));
EXPECT_EQ(got[1], ColorTheme4b(134, 57, 33, 26));
@ -99,7 +100,7 @@ TEST(imbuf_transform, cubic_bspline_2x_smaller)
TEST(imbuf_transform, cubic_mitchell_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_MITCHELL, 1);
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_MITCHELL);
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
EXPECT_EQ(got[0], ColorTheme4b(195, 130, 67, 255));
EXPECT_EQ(got[1], ColorTheme4b(132, 51, 28, 0));
@ -109,7 +110,7 @@ TEST(imbuf_transform, cubic_mitchell_2x_smaller)
TEST(imbuf_transform, cubic_mitchell_fractional_larger)
{
ImBuf *res = transform_fractional_larger(IMB_FILTER_CUBIC_MITCHELL, 1);
ImBuf *res = transform_fractional_larger(IMB_FILTER_CUBIC_MITCHELL);
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
EXPECT_EQ(got[0 + 0 * res->x], ColorTheme4b(0, 0, 0, 255));
EXPECT_EQ(got[1 + 0 * res->x], ColorTheme4b(127, 0, 0, 255));
@ -138,8 +139,7 @@ TEST(imbuf_transform, nearest_very_large_scale)
ImBuf *res = IMB_allocImBuf(3841, 1, 32, IB_rect);
float4x4 matrix = math::from_loc_rot_scale<float4x4>(
float3(254, 0, 0), math::Quaternion::identity(), float3(3.0f / 3840.0f, 1, 1));
IMB_transform(
src, res, IMB_TRANSFORM_MODE_REGULAR, IMB_FILTER_NEAREST, 1, matrix.ptr(), nullptr);
IMB_transform(src, res, IMB_TRANSFORM_MODE_REGULAR, IMB_FILTER_NEAREST, matrix.ptr(), nullptr);
/* Check result: leftmost red, middle green, two rightmost pixels blue and black.
* If the transform code internally does not have enough precision while stepping

View File

@ -163,6 +163,8 @@ typedef struct BrushCurvesSculptSettings {
struct CurveMapping *curve_parameter_falloff;
} BrushCurvesSculptSettings;
/** Max number of propagation steps for automasking settings.*/
#define AUTOMASKING_BOUNDARY_EDGES_MAX_PROPAGATION_STEPS 20
typedef struct Brush {
DNA_DEFINE_CXX_METHODS(Brush)

View File

@ -835,7 +835,7 @@
.factor = 1.0f, \
.step = 1, \
}
#define _DNA_DEFAULT_GreasePencilOffsetModifierData \
{ \
.flag = 0, \
@ -847,4 +847,17 @@
.stroke_start_offset = 0, \
}
#define _DNA_DEFAULT_GreasePencilNoiseModifierData \
{ \
.flag = GP_NOISE_FULL_STROKE | GP_NOISE_USE_RANDOM, \
.factor = 0.5f, \
.factor_strength = 0.0f, \
.factor_thickness = 0.0f, \
.factor_uvs = 0.0f, \
.noise_scale = 0.0f, \
.noise_offset = 0.0f, \
.step = 4, \
.seed = 1, \
}
/* clang-format off */

View File

@ -99,6 +99,7 @@ typedef enum ModifierType {
eModifierType_GreasePencilTint = 64,
eModifierType_GreasePencilSmooth = 65,
eModifierType_GreasePencilOffset = 66,
eModifierType_GreasePencilNoise = 67,
NUM_MODIFIER_TYPES,
} ModifierType;
@ -2667,3 +2668,28 @@ typedef enum GreasePencilOffsetModifierMode {
MOD_GREASE_PENCIL_OFFSET_MATERIAL = 2,
MOD_GREASE_PENCIL_OFFSET_STROKE = 3,
} GreasePencilOffsetModifierMode;
typedef struct GreasePencilNoiseModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
/** For convenience of versioning, these flags are kept in `eNoiseGpencil_Flag`. */
int flag;
/** Factor of noise. */
float factor;
float factor_strength;
float factor_thickness;
float factor_uvs;
/** Noise Frequency scaling */
float noise_scale;
float noise_offset;
short noise_mode;
char _pad[2];
/** How many frames before recalculate randoms. */
int step;
/** Random seed */
int seed;
void *_pad1;
} GreasePencilNoiseModifierData;

View File

@ -1659,7 +1659,7 @@ typedef struct NodeEnumDefinition {
NodeEnumItem *add_item(blender::StringRef name);
bool remove_item(NodeEnumItem &item);
void clear();
bool move_item(uint16_t from_index, uint16_t to_index);
bool move_item(int from_index, int to_index);
const NodeEnumItem *active_item() const;
NodeEnumItem *active_item();

View File

@ -418,6 +418,7 @@
.automasking_start_normal_falloff = 0.25f, \
.automasking_view_normal_limit = 1.570796, /* 0.5 * pi. */ \
.automasking_view_normal_falloff = 0.25f, \
.automasking_boundary_edges_propagation_steps = 1, \
.flags = SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE,\
.paint = {\
.symmetry_flags = PAINT_SYMMETRY_FEATHER,\

View File

@ -1114,9 +1114,9 @@ typedef struct Sculpt {
float constant_detail;
float detail_percent;
int automasking_boundary_edges_propagation_steps;
int automasking_cavity_blur_steps;
float automasking_cavity_factor;
char _pad[4];
float automasking_start_normal_limit, automasking_start_normal_falloff;
float automasking_view_normal_limit, automasking_view_normal_falloff;

View File

@ -838,7 +838,7 @@ typedef enum SequenceColorTag {
enum {
SEQ_TRANSFORM_FILTER_NEAREST = 0,
SEQ_TRANSFORM_FILTER_BILINEAR = 1,
SEQ_TRANSFORM_FILTER_NEAREST_3x3 = 2,
SEQ_TRANSFORM_FILTER_BOX = 2,
SEQ_TRANSFORM_FILTER_CUBIC_BSPLINE = 3,
SEQ_TRANSFORM_FILTER_CUBIC_MITCHELL = 4,
};

View File

@ -293,6 +293,7 @@ SDNA_DEFAULT_DECL_STRUCT(WeightVGProximityModifierData);
SDNA_DEFAULT_DECL_STRUCT(WeldModifierData);
SDNA_DEFAULT_DECL_STRUCT(WireframeModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilSubdivModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilNoiseModifierData);
/* Grease Pencil 3.0 modifiers. */
SDNA_DEFAULT_DECL_STRUCT(GreasePencilSmoothModifierData);
@ -545,6 +546,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(WeldModifierData),
SDNA_DEFAULT_DECL(WireframeModifierData),
SDNA_DEFAULT_DECL(GreasePencilSubdivModifierData),
SDNA_DEFAULT_DECL(GreasePencilNoiseModifierData),
/* Grease Pencil 3.0 defaults. */
SDNA_DEFAULT_DECL(GreasePencilSmoothModifierData),

View File

@ -3206,8 +3206,8 @@ static void rna_def_brush(BlenderRNA *brna)
prop = RNA_def_property(
srna, "automasking_boundary_edges_propagation_steps", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "automasking_boundary_edges_propagation_steps");
RNA_def_property_range(prop, 1, 20);
RNA_def_property_ui_range(prop, 1, 20, 1, 3);
RNA_def_property_range(prop, 1, AUTOMASKING_BOUNDARY_EDGES_MAX_PROPAGATION_STEPS);
RNA_def_property_ui_range(prop, 1, AUTOMASKING_BOUNDARY_EDGES_MAX_PROPAGATION_STEPS, 1, -1);
RNA_def_property_ui_text(prop,
"Propagation Steps",
"Distance where boundary edge automasking is going to protect vertices "

View File

@ -306,6 +306,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_OFFSET,
"Offset",
"Change stroke location, rotation, or scale"},
{eModifierType_GreasePencilNoise,
"GREASE_PENCIL_NOISE",
ICON_MOD_NOISE,
"Noise",
"Generate noise wobble in grease pencil strokes"},
RNA_ENUM_ITEM_HEADING(N_("Physics"), nullptr),
{eModifierType_Cloth, "CLOTH", ICON_MOD_CLOTH, "Cloth", ""},
@ -1829,12 +1834,14 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOpacity);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilSubdiv);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilTint);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilSmooth);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilNoise);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilSubdiv);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilTint);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilSmooth);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilNoise);
static void rna_GreasePencilOpacityModifier_opacity_factor_range(
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
@ -8129,6 +8136,97 @@ static void rna_def_modifier_grease_pencil_offset(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_noise(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem modifier_noise_random_mode_items[] = {
{GP_NOISE_RANDOM_STEP, "STEP", 0, "Steps", "Randomize every number of frames"},
{GP_NOISE_RANDOM_KEYFRAME, "KEYFRAME", 0, "Keyframes", "Randomize on keyframes only"},
{0, nullptr, 0, nullptr, nullptr},
};
srna = RNA_def_struct(brna, "GreasePencilNoiseModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Grease Pencil Noise Modifier", "Noise effect modifier");
RNA_def_struct_sdna(srna, "GreasePencilNoiseModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_NOISE);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilNoiseModifier_material_filter_set");
rna_def_modifier_grease_pencil_vertex_group(
srna, "rna_GreasePencilNoiseModifier_vertex_group_name_set");
rna_def_modifier_grease_pencil_custom_curve(srna);
rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0);
rna_def_modifier_panel_open_prop(srna, "open_random_panel", 1);
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "factor");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
RNA_def_property_ui_text(prop, "Offset Factor", "Amount of noise to apply");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "factor_strength", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "factor_strength");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
RNA_def_property_ui_text(prop, "Strength Factor", "Amount of noise to apply to opacity");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "factor_thickness", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "factor_thickness");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
RNA_def_property_ui_text(prop, "Thickness Factor", "Amount of noise to apply to thickness");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "factor_uvs", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "factor_uvs");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
RNA_def_property_ui_text(prop, "UV Factor", "Amount of noise to apply to UV rotation");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_random", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_NOISE_USE_RANDOM);
RNA_def_property_ui_text(prop, "Random", "Use random values over time");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED);
RNA_def_property_ui_text(prop, "Noise Seed", "Random seed");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "noise_scale", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "noise_scale");
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_ui_text(prop, "Noise Scale", "Scale the noise frequency");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "noise_offset", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "noise_offset");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 100.0, 0.1, 3);
RNA_def_property_ui_text(prop, "Noise Offset", "Offset the noise along the strokes");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "step");
RNA_def_property_range(prop, 1, 100);
RNA_def_property_ui_text(prop, "Step", "Number of frames between randomization steps");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "random_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "noise_mode");
RNA_def_property_enum_items(prop, modifier_noise_random_mode_items);
RNA_def_property_ui_text(prop, "Mode", "Where to perform randomization");
RNA_define_lib_overridable(false);
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -8295,6 +8393,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_tint(brna);
rna_def_modifier_grease_pencil_smooth(brna);
rna_def_modifier_grease_pencil_offset(brna);
rna_def_modifier_grease_pencil_noise(brna);
}
#endif

View File

@ -425,7 +425,7 @@ const EnumPropertyItem *RNA_node_enum_definition_itemf(
for (const blender::bke::RuntimeNodeEnumItem &item : enum_items.items) {
tmp.value = item.identifier;
/* Item name is unique and used as the RNA identitifier as well.
/* Item name is unique and used as the RNA identifier as well.
* The integer value is persistent and unique and should be used
* when storing the enum value. */
tmp.identifier = item.name.c_str();

View File

@ -883,6 +883,17 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr);
} while ((++entry)->identifier);
prop = RNA_def_property(
srna, "automasking_boundary_edges_propagation_steps", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "automasking_boundary_edges_propagation_steps");
RNA_def_property_range(prop, 1, AUTOMASKING_BOUNDARY_EDGES_MAX_PROPAGATION_STEPS);
RNA_def_property_ui_range(prop, 1, AUTOMASKING_BOUNDARY_EDGES_MAX_PROPAGATION_STEPS, 1, -1);
RNA_def_property_ui_text(prop,
"Propagation Steps",
"Distance where boundary edge automasking is going to protect vertices "
"from the fully masked edge");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr);
prop = RNA_def_property(srna, "automasking_cavity_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "automasking_cavity_factor");
RNA_def_property_ui_text(prop, "Cavity Factor", "The contrast of the cavity mask");

View File

@ -1726,11 +1726,11 @@ static const EnumPropertyItem transform_filter_items[] = {
"Cubic B-Spline",
"Cubic B-Spline filter (blurry but no ringing) on 4" BLI_STR_UTF8_MULTIPLICATION_SIGN
"4 samples"},
{SEQ_TRANSFORM_FILTER_NEAREST_3x3,
"SUBSAMPLING_3x3",
{SEQ_TRANSFORM_FILTER_BOX,
"BOX",
0,
"Subsampling (3" BLI_STR_UTF8_MULTIPLICATION_SIGN "3)",
"Use nearest with 3" BLI_STR_UTF8_MULTIPLICATION_SIGN "3 subsamples"},
"Box",
"Averages source image samples that fall under destination pixel"},
{0, nullptr, 0, nullptr, nullptr},
};

View File

@ -209,10 +209,22 @@ static void rna_progress_end(wmWindowManager *wm)
}
/* wrap these because of 'const wmEvent *' */
static int rna_Operator_confirm(bContext *C, wmOperator *op, wmEvent *event)
static int rna_Operator_confirm(bContext *C,
wmOperator *op,
wmEvent * /*event*/,
const char *title,
const char *message,
const char *confirm_text,
const int icon,
const char *text_ctxt,
const bool translate)
{
return WM_operator_confirm(C, op, event);
title = RNA_translate_ui_text(title, text_ctxt, nullptr, nullptr, translate);
message = RNA_translate_ui_text(message, text_ctxt, nullptr, nullptr, translate);
confirm_text = RNA_translate_ui_text(confirm_text, text_ctxt, nullptr, nullptr, translate);
return WM_operator_confirm_ex(C, op, title, message, confirm_text, icon);
}
static int rna_Operator_props_popup(bContext *C, wmOperator *op, wmEvent *event)
{
return WM_operator_props_popup(C, op, event);
@ -788,6 +800,16 @@ void RNA_api_window(StructRNA *srna)
RNA_def_function_return(func, parm);
}
const EnumPropertyItem rna_operator_popup_icon_items[] = {
{ALERT_ICON_NONE, "NONE", 0, "None", ""},
{ALERT_ICON_WARNING, "WARNING", 0, "Warning", ""},
{ALERT_ICON_QUESTION, "QUESTION", 0, "Question", ""},
{ALERT_ICON_ERROR, "ERROR", 0, "Error", ""},
{ALERT_ICON_INFO, "INFO", 0, "Info", ""},
{ALERT_ICON_BLENDER, "BLENDER", 0, "Blender", ""},
{0, nullptr, 0, nullptr, nullptr},
};
void RNA_api_wm(StructRNA *srna)
{
FunctionRNA *func;
@ -911,6 +933,24 @@ void RNA_api_wm(StructRNA *srna)
"(only to let user confirm the execution, no operator properties shown)");
rna_generic_op_invoke(func, WM_GEN_INVOKE_EVENT | WM_GEN_INVOKE_RETURN);
parm = RNA_def_property(func, "title", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(parm, "Title", "Optional text to show as title of the popup");
parm = RNA_def_property(func, "message", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(parm, "Message", "Optional first line of content text");
parm = RNA_def_property(func, "confirm_text", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(
parm,
"Confirm Text",
"Optional text to show instead to the default \"OK\" confirmation button text");
parm = RNA_def_property(func, "icon", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(parm, rna_operator_popup_icon_items);
RNA_def_property_ui_text(parm, "Icon", "Optional icon displayed in the dialog");
api_ui_item_common_translation(func);
/* wrap UI_popup_menu_begin */
func = RNA_def_function(srna, "popmenu_begin__internal", "rna_PopMenuBegin");
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);

View File

@ -45,6 +45,7 @@ set(SRC
intern/MOD_explode.cc
intern/MOD_fluid.cc
intern/MOD_grease_pencil_color.cc
intern/MOD_grease_pencil_noise.cc
intern/MOD_grease_pencil_offset.cc
intern/MOD_grease_pencil_opacity.cc
intern/MOD_grease_pencil_smooth.cc

Some files were not shown because too many files have changed in this diff Show More