BKE: Utilities to create curves and point clouds without attributes #120368

Open
Iliya Katushenock wants to merge 16 commits from mod_moder/blender:more_no_att_constructors into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
618 changed files with 12869 additions and 6255 deletions
Showing only changes of commit 8921d2159a - Show all commits

View File

@ -99,6 +99,8 @@ CHECKER_EXCLUDE_FROM_SUMMARY = {
"nonoOperatorEq",
# There seems to be many false positives here.
"unusedFunction",
# Also noisy, looks like these are not issues to "solve".
"unusedPrivateFunction",
# TODO: consider enabling this, more of a preference,
# not using STL algorithm's doesn't often hint at actual errors.
"useStlAlgorithm",

View File

@ -95,6 +95,7 @@ ccl_device void kernel_curve_shadow_transparency_evaluate(
ccl_global float *output,
const int offset)
{
#ifdef __HAIR__
/* Setup shader data. */
const KernelShaderEvalInput in = input[offset];
@ -108,6 +109,7 @@ ccl_device void kernel_curve_shadow_transparency_evaluate(
/* Write output. */
output[offset] = clamp(average(surface_shader_transparency(kg, &sd)), 0.0f, 1.0f);
#endif
}
CCL_NAMESPACE_END

View File

@ -175,7 +175,7 @@ ccl_device_inline
break;
}
#endif
#if BVH_FEATURE(BVH_HAIR)
#if BVH_FEATURE(BVH_HAIR) && defined(__HAIR__)
case PRIMITIVE_CURVE_THICK:
case PRIMITIVE_MOTION_CURVE_THICK:
case PRIMITIVE_CURVE_RIBBON:
@ -195,7 +195,7 @@ ccl_device_inline
break;
}
#endif
#if BVH_FEATURE(BVH_POINTCLOUD)
#if BVH_FEATURE(BVH_POINTCLOUD) && defined(__POINTCLOUD__)
case PRIMITIVE_POINT:
case PRIMITIVE_MOTION_POINT: {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {

View File

@ -178,7 +178,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
break;
}
#endif /* BVH_FEATURE(BVH_MOTION) */
#if BVH_FEATURE(BVH_HAIR)
#if BVH_FEATURE(BVH_HAIR) && defined(__HAIR__)
case PRIMITIVE_CURVE_THICK:
case PRIMITIVE_MOTION_CURVE_THICK:
case PRIMITIVE_CURVE_RIBBON:
@ -201,7 +201,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
break;
}
#endif /* BVH_FEATURE(BVH_HAIR) */
#if BVH_FEATURE(BVH_POINTCLOUD)
#if BVH_FEATURE(BVH_POINTCLOUD) && defined(__POINTCLOUD__)
case PRIMITIVE_POINT:
case PRIMITIVE_MOTION_POINT: {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {

View File

@ -166,13 +166,13 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
label = bsdf_microfacet_ggx_sample(
sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
kg, sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
label = bsdf_microfacet_beckmann_sample(
sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
kg, sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
label = bsdf_ashikhmin_shirley_sample(
@ -506,12 +506,12 @@ ccl_device_inline
/* For consistency with eval() this should be using sd->Ng, but that causes
* artifacts (see shadow_terminator_metal test). Needs deeper investigation
* for how to solve this. */
eval = bsdf_microfacet_ggx_eval(sc, sd->N, sd->wi, wo, pdf);
eval = bsdf_microfacet_ggx_eval(kg, sc, sd->N, sd->wi, wo, pdf);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
eval = bsdf_microfacet_beckmann_eval(sc, sd->N, sd->wi, wo, pdf);
eval = bsdf_microfacet_beckmann_eval(kg, sc, sd->N, sd->wi, wo, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
eval = bsdf_ashikhmin_shirley_eval(sc, sd->N, sd->wi, wo, pdf);

View File

@ -29,7 +29,14 @@ enum MicrofacetFresnel {
F82_TINT,
};
typedef struct FresnelThinFilm {
float thickness;
float ior;
} FresnelThinFilm;
typedef struct FresnelDielectricTint {
FresnelThinFilm thin_film;
Spectrum reflection_tint;
Spectrum transmission_tint;
} FresnelDielectricTint;
@ -39,6 +46,8 @@ typedef struct FresnelConductor {
} FresnelConductor;
typedef struct FresnelGeneralizedSchlick {
FresnelThinFilm thin_film;
Spectrum reflection_tint;
Spectrum transmission_tint;
/* Reflectivity at perpendicular (F0) and glancing (F90) angles. */
@ -217,7 +226,8 @@ ccl_device_forceinline float3 microfacet_ggx_sample_vndf(const float3 wi,
* the incoming angle `cos_theta_i`.
* Also returns the cosine of the angle between the normal and the refracted ray as `r_cos_theta_t`
* if provided. */
ccl_device_forceinline void microfacet_fresnel(ccl_private const MicrofacetBsdf *bsdf,
ccl_device_forceinline void microfacet_fresnel(KernelGlobals kg,
ccl_private const MicrofacetBsdf *bsdf,
const float cos_theta_i,
ccl_private float *r_cos_theta_t,
ccl_private Spectrum *r_reflectance,
@ -258,13 +268,41 @@ ccl_device_forceinline void microfacet_fresnel(ccl_private const MicrofacetBsdf
else if (bsdf->fresnel_type == MicrofacetFresnel::GENERALIZED_SCHLICK) {
ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *)
bsdf->fresnel;
float s;
if (fresnel->exponent < 0.0f) {
Spectrum F;
if (fresnel->thin_film.thickness > 0.1f) {
/* Iridescence doesn't combine well with the general case. We only expose it through the
* Principled BSDF for now, so it's fine to not support custom exponents and F90. */
kernel_assert(fresnel->exponent < 0.0f);
kernel_assert(fresnel->f90 == one_spectrum());
F = fresnel_iridescence(kg,
1.0f,
fresnel->thin_film.ior,
bsdf->ior,
cos_theta_i,
fresnel->thin_film.thickness,
r_cos_theta_t);
/* Apply F0 scaling (here per-channel, since iridescence produces colored output).
* Note that the usual approach (as used below) cannot be used here, since F may be below
* F0_real. Therefore, use a different approach: Scale the result by (F0 / F0_real), with
* the strength of the scaling depending on how close F is to F0_real.
* There isn't one single "correct" way to do this, it's just for artistic control anyways.
*/
const float F0_real = F0_from_ior(bsdf->ior);
if (F0_real > 1e-5f && !isequal(F, one_spectrum())) {
FOREACH_SPECTRUM_CHANNEL (i) {
const float s = saturatef(inverse_lerp(1.0f, F0_real, GET_SPECTRUM_CHANNEL(F, i)));
const float factor = GET_SPECTRUM_CHANNEL(fresnel->f0, i) / F0_real;
GET_SPECTRUM_CHANNEL(F, i) *= mix(1.0f, factor, s);
}
}
}
else if (fresnel->exponent < 0.0f) {
/* Special case: Use real Fresnel curve to determine the interpolation between F0 and F90.
* Used by Principled v1. */
* Used by Principled BSDF. */
const float F_real = fresnel_dielectric(cos_theta_i, bsdf->ior, r_cos_theta_t);
const float F0_real = F0_from_ior(bsdf->ior);
s = saturatef(inverse_lerp(F0_real, 1.0f, F_real));
const float s = saturatef(inverse_lerp(F0_real, 1.0f, F_real));
F = mix(fresnel->f0, fresnel->f90, s);
}
else {
/* Regular case: Generalized Schlick term. */
@ -282,9 +320,10 @@ ccl_device_forceinline void microfacet_fresnel(ccl_private const MicrofacetBsdf
/* TODO(lukas): Is a special case for exponent==5 worth it? */
/* When going from a higher to a lower IOR, we must use the transmitted angle. */
s = powf(1.0f - ((bsdf->ior < 1.0f) ? cos_theta_t : cos_theta_i), fresnel->exponent);
const float fresnel_angle = ((bsdf->ior < 1.0f) ? cos_theta_t : cos_theta_i);
const float s = powf(1.0f - fresnel_angle, fresnel->exponent);
F = mix(fresnel->f0, fresnel->f90, s);
}
const Spectrum F = mix(fresnel->f0, fresnel->f90, s);
*r_reflectance = F * fresnel->reflection_tint;
*r_transmittance = (one_spectrum() - F) * fresnel->transmission_tint;
}
@ -374,7 +413,7 @@ ccl_device Spectrum bsdf_microfacet_estimate_albedo(KernelGlobals kg,
{
const float cos_NI = dot(sd->wi, bsdf->N);
Spectrum reflectance, transmittance;
microfacet_fresnel(bsdf, cos_NI, nullptr, &reflectance, &transmittance);
microfacet_fresnel(kg, bsdf, cos_NI, nullptr, &reflectance, &transmittance);
reflectance *= (float)eval_reflection;
transmittance *= (float)eval_transmission;
@ -384,19 +423,25 @@ ccl_device Spectrum bsdf_microfacet_estimate_albedo(KernelGlobals kg,
ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *)
bsdf->fresnel;
float rough = sqrtf(sqrtf(bsdf->alpha_x * bsdf->alpha_y));
float s;
if (fresnel->exponent < 0.0f) {
float z = sqrtf(fabsf((bsdf->ior - 1.0f) / (bsdf->ior + 1.0f)));
s = lookup_table_read_3D(
kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_ior_s, 16, 16, 16);
if (fresnel->thin_film.thickness > 0.1f) {
/* Precomputing LUTs for thin-film iridescence isn't viable, so fall back to the specular
* reflection approximation from the microfacet_fresnel call above in that case. */
}
else {
float z = 1.0f / (0.2f * fresnel->exponent + 1.0f);
s = lookup_table_read_3D(
kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_s, 16, 16, 16);
float rough = sqrtf(sqrtf(bsdf->alpha_x * bsdf->alpha_y));
float s;
if (fresnel->exponent < 0.0f) {
float z = sqrtf(fabsf((bsdf->ior - 1.0f) / (bsdf->ior + 1.0f)));
s = lookup_table_read_3D(
kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_ior_s, 16, 16, 16);
}
else {
float z = 1.0f / (0.2f * fresnel->exponent + 1.0f);
s = lookup_table_read_3D(
kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_s, 16, 16, 16);
}
reflectance = mix(fresnel->f0, fresnel->f90, s) * fresnel->reflection_tint;
}
reflectance = mix(fresnel->f0, fresnel->f90, s) * fresnel->reflection_tint;
}
else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) {
ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel;
@ -498,7 +543,8 @@ ccl_device_forceinline int bsdf_microfacet_eval_flag(const ccl_private Microface
}
template<MicrofacetType m_type>
ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
ccl_device Spectrum bsdf_microfacet_eval(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
const float3 Ng,
const float3 wi,
const float3 wo,
@ -544,7 +590,7 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
/* Compute Fresnel coefficients. */
const float cos_HI = dot(H, wi);
Spectrum reflectance, transmittance;
microfacet_fresnel(bsdf, cos_HI, nullptr, &reflectance, &transmittance);
microfacet_fresnel(kg, bsdf, cos_HI, nullptr, &reflectance, &transmittance);
if (is_zero(reflectance) && is_zero(transmittance)) {
return zero_spectrum();
@ -587,7 +633,8 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
}
template<MicrofacetType m_type>
ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
ccl_device int bsdf_microfacet_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 wi,
const float3 rand,
@ -647,7 +694,7 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
float cos_HO;
/* Compute Fresnel coefficients. */
Spectrum reflectance, transmittance;
microfacet_fresnel(bsdf, cos_HI, &cos_HO, &reflectance, &transmittance);
microfacet_fresnel(kg, bsdf, cos_HI, &cos_HO, &reflectance, &transmittance);
if (is_zero(reflectance) && is_zero(transmittance)) {
return LABEL_NONE;
@ -920,17 +967,19 @@ ccl_device void bsdf_microfacet_blur(ccl_private ShaderClosure *sc, float roughn
bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
}
ccl_device Spectrum bsdf_microfacet_ggx_eval(ccl_private const ShaderClosure *sc,
ccl_device Spectrum bsdf_microfacet_ggx_eval(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
const float3 Ng,
const float3 wi,
const float3 wo,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
return bsdf->energy_scale * bsdf_microfacet_eval<MicrofacetType::GGX>(sc, Ng, wi, wo, pdf);
return bsdf->energy_scale * bsdf_microfacet_eval<MicrofacetType::GGX>(kg, sc, Ng, wi, wo, pdf);
}
ccl_device int bsdf_microfacet_ggx_sample(ccl_private const ShaderClosure *sc,
ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 wi,
const float3 rand,
@ -942,7 +991,7 @@ ccl_device int bsdf_microfacet_ggx_sample(ccl_private const ShaderClosure *sc,
{
int label = bsdf_microfacet_sample<MicrofacetType::GGX>(
sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta);
kg, sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta);
*eval *= ((ccl_private const MicrofacetBsdf *)sc)->energy_scale;
return label;
}
@ -985,16 +1034,18 @@ ccl_device int bsdf_microfacet_beckmann_glass_setup(ccl_private MicrofacetBsdf *
return SD_BSDF | SD_BSDF_HAS_TRANSMISSION | bsdf_microfacet_eval_flag(bsdf);
}
ccl_device Spectrum bsdf_microfacet_beckmann_eval(ccl_private const ShaderClosure *sc,
ccl_device Spectrum bsdf_microfacet_beckmann_eval(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
const float3 Ng,
const float3 wi,
const float3 wo,
ccl_private float *pdf)
{
return bsdf_microfacet_eval<MicrofacetType::BECKMANN>(sc, Ng, wi, wo, pdf);
return bsdf_microfacet_eval<MicrofacetType::BECKMANN>(kg, sc, Ng, wi, wo, pdf);
}
ccl_device int bsdf_microfacet_beckmann_sample(ccl_private const ShaderClosure *sc,
ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 wi,
const float3 rand,
@ -1005,7 +1056,7 @@ ccl_device int bsdf_microfacet_beckmann_sample(ccl_private const ShaderClosure *
ccl_private float *eta)
{
return bsdf_microfacet_sample<MicrofacetType::BECKMANN>(
sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta);
kg, sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta);
}
CCL_NAMESPACE_END

View File

@ -34,6 +34,9 @@ ccl_device void bsdf_ray_portal_setup(ccl_private ShaderData *sd,
sd, sizeof(RayPortalClosure), CLOSURE_BSDF_RAY_PORTAL_ID, weight);
if (pc) {
if (is_zero(direction)) {
direction = -sd->wi;
}
pc->sample_weight = sample_weight;
pc->N = sd->N;
pc->P = position;

View File

@ -9,9 +9,14 @@
CCL_NAMESPACE_BEGIN
/* Compute fresnel reflectance. Also return the dot product of the refracted ray and the normal as
* `cos_theta_t`, as it is used when computing the direction of the refracted ray. */
ccl_device float fresnel_dielectric(float cos_theta_i, float eta, ccl_private float *r_cos_theta_t)
/* Compute fresnel reflectance for perpendicular (aka S-) and parallel (aka P-) polarized light.
* If requested by the caller, r_phi is set to the phase shift on reflection.
* Also returns the dot product of the refracted ray and the normal as `cos_theta_t`, as it is
* used when computing the direction of the refracted ray. */
ccl_device float2 fresnel_dielectric_polarized(float cos_theta_i,
float eta,
ccl_private float *r_cos_theta_t,
ccl_private float2 *r_phi)
{
kernel_assert(!isnan_safe(cos_theta_i));
@ -20,7 +25,18 @@ ccl_device float fresnel_dielectric(float cos_theta_i, float eta, ccl_private fl
const float eta_cos_theta_t_sq = sqr(eta) - (1.0f - sqr(cos_theta_i));
if (eta_cos_theta_t_sq <= 0) {
/* Total internal reflection. */
return 1.0f;
if (r_phi) {
/* The following code would compute the proper phase shift on TIR.
* However, for the current user of this computation (the iridescence code),
* this doesn't actually affect the result, so don't bother with the computation for now.
*
* `const float fac = sqrtf(1.0f - sqr(cosThetaI) - sqr(eta));`
* `r_phi->x = -2.0f * atanf(fac / cosThetaI);`
* `r_phi->y = -2.0f * atanf(fac / (cosThetaI * sqr(eta)));`
*/
*r_phi = zero_float2();
}
return one_float2();
}
cos_theta_i = fabsf(cos_theta_i);
@ -33,9 +49,22 @@ ccl_device float fresnel_dielectric(float cos_theta_i, float eta, ccl_private fl
/* Amplitudes of reflected waves. */
const float r_s = (cos_theta_i + eta * cos_theta_t) / (cos_theta_i - eta * cos_theta_t);
const float r_p = (cos_theta_t + eta * cos_theta_i) / (cos_theta_t - eta * cos_theta_i);
const float r_p = (cos_theta_t + eta * cos_theta_i) / (eta * cos_theta_i - cos_theta_t);
return 0.5f * (sqr(r_s) + sqr(r_p));
if (r_phi) {
*r_phi = make_float2(r_s < 0.0f, r_p < 0.0f) * M_PI_F;
}
/* Return squared amplitude to get the fraction of reflected energy. */
return make_float2(sqr(r_s), sqr(r_p));
}
/* Compute fresnel reflectance for unpolarized light. */
ccl_device_forceinline float fresnel_dielectric(float cos_theta_i,
float eta,
ccl_private float *r_cos_theta_t)
{
return average(fresnel_dielectric_polarized(cos_theta_i, eta, r_cos_theta_t, nullptr));
}
/* Refract the incident ray, given the cosine of the refraction angle and the relative refractive
@ -235,4 +264,124 @@ ccl_device_inline Spectrum closure_layering_weight(const Spectrum layer_albedo,
return weight * saturatef(1.0f - reduce_max(safe_divide_color(layer_albedo, weight)));
}
/* ******** Thin-film iridescence implementation ********
*
* Based on "A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence"
* by Laurent Belcour and Pascal Barla.
* https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html.
*/
/**
* Evaluate the sensitivity functions for the Fourier-space spectral integration.
* The code here uses the Gaussian fit for the CIE XYZ curves that is provided
* in the reference implementation.
* For details on what this actually represents, see the paper.
* In theory we should pre-compute the sensitivity functions for the working RGB
* color-space, remap them to be functions of (light) frequency, take their Fourier
* transform and store them as a LUT that gets looked up here.
* In practice, using the XYZ fit and converting the result from XYZ to RGB is easier.
*/
ccl_device_inline Spectrum iridescence_lookup_sensitivity(float OPD, float shift)
{
float phase = M_2PI_F * OPD * 1e-9f;
float3 val = make_float3(5.4856e-13f, 4.4201e-13f, 5.2481e-13f);
float3 pos = make_float3(1.6810e+06f, 1.7953e+06f, 2.2084e+06f);
float3 var = make_float3(4.3278e+09f, 9.3046e+09f, 6.6121e+09f);
float3 xyz = val * sqrt(M_2PI_F * var) * cos(pos * phase + shift) * exp(-sqr(phase) * var);
xyz.x += 1.64408e-8f * cosf(2.2399e+06f * phase + shift) * expf(-4.5282e+09f * sqr(phase));
return xyz / 1.0685e-7f;
}
ccl_device_inline float3
iridescence_airy_summation(float T121, float R12, float R23, float OPD, float phi)
{
if (R23 == 1.0f) {
/* Shortcut for TIR on the bottom interface. */
return one_float3();
}
float R123 = R12 * R23;
float r123 = sqrtf(R123);
float Rs = sqr(T121) * R23 / (1.0f - R123);
/* Perform summation over path order differences (equation 10). */
float3 R = make_float3(R12 + Rs); /* C0 */
float Cm = (Rs - T121);
/* Truncate after m=3, higher differences have barely any impact. */
for (int m = 1; m < 4; m++) {
Cm *= r123;
R += Cm * 2.0f * iridescence_lookup_sensitivity(m * OPD, m * phi);
}
return R;
}
ccl_device Spectrum fresnel_iridescence(KernelGlobals kg,
float eta1,
float eta2,
float eta3,
float cos_theta_1,
float thickness,
ccl_private float *r_cos_theta_3)
{
/* For films below 30nm, the wave-optic-based Airy summation approach no longer applies,
* so blend towards the case without coating. */
if (thickness < 30.0f) {
eta2 = mix(eta1, eta2, smoothstep(0.0f, 30.0f, thickness));
}
float cos_theta_2;
float2 phi12, phi23;
/* Compute reflection at the top interface (ambient to film). */
float2 R12 = fresnel_dielectric_polarized(cos_theta_1, eta2 / eta1, &cos_theta_2, &phi12);
if (isequal(R12, one_float2())) {
/* TIR at the top interface. */
return one_spectrum();
}
/* Compute optical path difference inside the thin film. */
float OPD = -2.0f * eta2 * thickness * cos_theta_2;
/* Compute reflection at the bottom interface (film to medium). */
float2 R23 = fresnel_dielectric_polarized(-cos_theta_2, eta3 / eta2, r_cos_theta_3, &phi23);
if (isequal(R23, one_float2())) {
/* TIR at the bottom interface.
* All the Airy summation math still simplifies to 1.0 in this case. */
return one_spectrum();
}
/* Compute helper parameters. */
float2 T121 = one_float2() - R12;
float2 phi = make_float2(M_PI_F, M_PI_F) - phi12 + phi23;
/* Perform Airy summation and average the polarizations. */
float3 R = mix(iridescence_airy_summation(T121.x, R12.x, R23.x, OPD, phi.x),
iridescence_airy_summation(T121.y, R12.y, R23.y, OPD, phi.y),
0.5f);
/* Color space conversion here is tricky.
* In theory, the correct thing would be to compute the spectral color matching functions
* for the RGB channels, take their Fourier transform in wavelength parametrization, and
* then use that in iridescence_lookup_sensitivity().
* To avoid this complexity, the code here instead uses the reference implementation's
* Gaussian fit of the CIE XYZ curves. However, this means that at this point, R is in
* XYZ values, not RGB.
* Additionally, since I is a reflectivity, not a luminance, the spectral color matching
* functions should be multiplied by the reference illuminant. Since the fit is based on
* the "raw" CIE XYZ curves, the reference illuminant implicitly is a constant spectrum,
* meaning Illuminant E.
* Therefore, we can't just use the regular XYZ->RGB conversion here, we need to include
* a chromatic adaption from E to whatever the white point of the working color space is.
* The proper way to do this would be a Von Kries-style transform, but to keep it simple,
* we just multiply by the white point here.
*
* Note: The reference implementation sidesteps all this by just hard-coding a XYZ->CIE RGB
* matrix. Since CIE RGB uses E as its white point, this sidesteps the chromatic adaption
* topic, but the primary colors don't match (unless you happen to actually work in CIE RGB.)
*/
R *= float4_to_float3(kernel_data.film.white_xyz);
return saturate(xyz_to_rgb(kg, R));
}
CCL_NAMESPACE_END

View File

@ -69,6 +69,7 @@ KERNEL_STRUCT_MEMBER(film, float4, xyz_to_r)
KERNEL_STRUCT_MEMBER(film, float4, xyz_to_g)
KERNEL_STRUCT_MEMBER(film, float4, xyz_to_b)
KERNEL_STRUCT_MEMBER(film, float4, rgb_to_y)
KERNEL_STRUCT_MEMBER(film, float4, white_xyz)
/* Rec709 to rendering color space. */
KERNEL_STRUCT_MEMBER(film, float4, rec709_to_r)
KERNEL_STRUCT_MEMBER(film, float4, rec709_to_g)

View File

@ -190,12 +190,14 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_global const int *path_index_array,
const int work_size)
{
# ifdef __VOLUME__
const int global_index = ccl_gpu_global_id_x();
if (ccl_gpu_kernel_within_bounds(global_index, work_size)) {
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
ccl_gpu_kernel_call(integrator_intersect_volume_stack(NULL, state));
}
# endif
}
ccl_gpu_kernel_postfix

View File

@ -39,6 +39,7 @@ struct MetalRTIntersectionShadowPayload {
bool result;
};
#ifdef __HAIR__
ccl_device_forceinline bool curve_ribbon_accept(
KernelGlobals kg, float u, float t, ccl_private const Ray *ray, int object, int prim, int type)
{
@ -63,11 +64,11 @@ ccl_device_forceinline bool curve_ribbon_accept(
float3 ray_D = ray->D;
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
float3 idir;
#if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
bvh_instance_motion_push(NULL, object, ray, &ray_P, &ray_D, &idir);
#else
# else
bvh_instance_push(NULL, object, ray, &ray_P, &ray_D, &idir);
#endif
# endif
}
/* ignore self intersections */
@ -78,11 +79,11 @@ ccl_device_forceinline bool curve_ribbon_accept(
ccl_device_forceinline float curve_ribbon_v(
KernelGlobals kg, float u, float t, ccl_private const Ray *ray, int object, int prim, int type)
{
#if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
float time = ray->time;
#else
# else
float time = 0.0f;
#endif
# endif
const bool is_motion = (type & PRIMITIVE_MOTION);
@ -108,11 +109,11 @@ ccl_device_forceinline float curve_ribbon_v(
float3 ray_D = ray->D;
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
float3 idir;
#if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
bvh_instance_motion_push(NULL, object, ray, &ray_P, &ray_D, &idir);
#else
# else
bvh_instance_push(NULL, object, ray, &ray_P, &ray_D, &idir);
#endif
# endif
}
const float4 P_curve4 = metal::catmull_rom(u, curve[0], curve[1], curve[2], curve[3]);
@ -130,6 +131,7 @@ ccl_device_forceinline float curve_ribbon_v(
float v = dot(P - P_curve, bitangent) / r_curve;
return clamp(v, -1.0, 1.0f);
}
#endif /* __HAIR__ */
/* Scene intersection. */
@ -207,6 +209,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
isect->u = intersection.triangle_barycentric_coord.x;
isect->v = intersection.triangle_barycentric_coord.y;
}
#ifdef __HAIR__
else if (kernel_data.bvh.have_curves && intersection.type == intersection_type::curve) {
int prim = intersection.primitive_id + intersection.user_instance_id;
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
@ -227,6 +230,8 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
isect->v = 0.0f;
}
}
#endif /* __HAIR__ */
#ifdef __POINTCLOUD__
else if (kernel_data.bvh.have_points && intersection.type == intersection_type::bounding_box) {
const int object = intersection.instance_id;
const uint prim = intersection.primitive_id + intersection.user_instance_id;
@ -234,11 +239,11 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
float3 idir;
#if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
bvh_instance_motion_push(NULL, object, ray, &r.origin, &r.direction, &idir);
#else
# else
bvh_instance_push(NULL, object, ray, &r.origin, &r.direction, &idir);
#endif
# endif
}
if (prim_type & PRIMITIVE_POINT) {
@ -262,6 +267,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
return true;
}
}
#endif /* __POINTCLOUD__ */
return true;
}
@ -497,6 +503,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
isect->object = intersection.instance_id;
isect->t = intersection.distance;
}
# ifdef __HAIR__
else if (kernel_data.bvh.have_curves && intersection.type == intersection_type::curve) {
int prim = intersection.primitive_id + intersection.user_instance_id;
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
@ -517,6 +524,8 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
isect->v = 0.0f;
}
}
# endif
# ifdef __POINTCLOUD__
else if (kernel_data.bvh.have_points && intersection.type == intersection_type::bounding_box) {
const int object = intersection.instance_id;
const uint prim = intersection.primitive_id + intersection.user_instance_id;
@ -526,11 +535,11 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
float3 idir;
# if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
bvh_instance_motion_push(NULL, object, ray, &r.origin, &r.direction, &idir);
# else
# else
bvh_instance_push(NULL, object, ray, &r.origin, &r.direction, &idir);
# endif
# endif
}
if (prim_type & PRIMITIVE_POINT) {
@ -552,6 +561,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
return true;
}
}
# endif
return true;
}

View File

@ -534,7 +534,6 @@ __anyhit__cycles_metalrt_visibility_test_box(const float ray_tmax [[max_distance
/* Primitive intersection functions. */
# ifdef __HAIR__
[[intersection(
curve, triangle_data, curve_data, METALRT_TAGS, extended_limits)]] PrimitiveIntersectionResult
__intersection__curve(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
@ -548,10 +547,10 @@ __intersection__curve(constant KernelParamsMetal &launch_params_metal [[buffer(1
float u [[curve_parameter]],
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]]
# if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
,
const float time [[time]]
# endif
# endif
)
{
uint prim = primitive_id + primitive_id_offset;
@ -559,9 +558,9 @@ __intersection__curve(constant KernelParamsMetal &launch_params_metal [[buffer(1
Ray ray;
ray.P = ray_P;
ray.D = ray_D;
# if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
ray.time = time;
# endif
# endif
PrimitiveIntersectionResult result =
metalrt_visibility_test<PrimitiveIntersectionResult, METALRT_HIT_CURVE>(
@ -582,9 +581,9 @@ __intersection__curve_shadow(constant KernelParamsMetal &launch_params_metal [[b
const float3 ray_D [[direction]],
float u [[curve_parameter]],
float t [[distance]],
# if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
const float time [[time]],
# endif
# endif
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]])
{
@ -595,9 +594,9 @@ __intersection__curve_shadow(constant KernelParamsMetal &launch_params_metal [[b
Ray ray;
ray.P = ray_P;
ray.D = ray_D;
# if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
ray.time = time;
# endif
# endif
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_CURVE>(
launch_params_metal, payload, object, prim, float2(u, 0), ray_tmax, t, &ray);
@ -605,7 +604,6 @@ __intersection__curve_shadow(constant KernelParamsMetal &launch_params_metal [[b
return result;
}
# endif /* __HAIR__ */
# ifdef __POINTCLOUD__
ccl_device_inline void metalrt_intersection_point(
@ -666,6 +664,8 @@ ccl_device_inline void metalrt_intersection_point_shadow(
}
}
# endif /* __POINTCLOUD__ */
[[intersection(bounding_box,
triangle_data,
curve_data,
@ -678,9 +678,9 @@ __intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1
const uint primitive_id_offset [[user_instance_id]],
const float3 ray_origin [[origin]],
const float3 ray_direction [[direction]],
# if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
const float time [[time]],
# endif
# endif
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]])
{
@ -692,6 +692,8 @@ __intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1
result.continue_search = true;
result.distance = ray_tmax;
# ifdef __POINTCLOUD__
metalrt_intersection_point(launch_params_metal,
payload,
object,
@ -708,6 +710,8 @@ __intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1
ray_tmax,
result);
# endif /* __POINTCLOUD__ */
return result;
}
@ -724,9 +728,9 @@ __intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[b
const uint primitive_id_offset [[user_instance_id]],
const float3 ray_origin [[origin]],
const float3 ray_direction [[direction]],
# if defined(__METALRT_MOTION__)
# if defined(__METALRT_MOTION__)
const float time [[time]],
# endif
# endif
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]])
{
@ -738,6 +742,8 @@ __intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[b
result.continue_search = true;
result.distance = ray_tmax;
# ifdef __POINTCLOUD__
metalrt_intersection_point_shadow(launch_params_metal,
payload,
object,
@ -754,7 +760,9 @@ __intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[b
ray_tmax,
result);
# endif /* __POINTCLOUD__ */
return result;
}
# endif /* __POINTCLOUD__ */
#endif /* __METALRT__ */
#endif /* __METALRT__ */

View File

@ -571,7 +571,9 @@ ccl_device_inline void film_write_transparent(KernelGlobals kg,
film_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent);
}
#ifdef __SHADOW_CATCHER__
film_write_shadow_catcher_transparent_only(kg, path_flag, transparent, buffer);
#endif
}
/* Write holdout to render buffer. */

View File

@ -202,13 +202,16 @@ ccl_device float curve_thickness(KernelGlobals kg, ccl_private const ShaderData
float4 P_curve[2];
if (!(sd->type & PRIMITIVE_MOTION)) {
# ifdef __OBJECT_MOTION__
if (sd->type & PRIMITIVE_MOTION) {
motion_curve_keys_linear(kg, sd->object, sd->time, k0, k1, P_curve);
}
else
# endif
{
P_curve[0] = kernel_data_fetch(curve_keys, k0);
P_curve[1] = kernel_data_fetch(curve_keys, k1);
}
else {
motion_curve_keys_linear(kg, sd->object, sd->time, k0, k1, P_curve);
}
r = (P_curve[1].w - P_curve[0].w) * sd->u + P_curve[0].w;
}

View File

@ -75,12 +75,14 @@ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals kg, int
return tfm;
}
#endif /* __OBJECT_MOTION__ */
ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals kg,
int object,
float time,
ccl_private Transform *itfm)
{
#ifdef __OBJECT_MOTION__
int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_MOTION) {
/* if we do motion blur */
@ -91,7 +93,9 @@ ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals kg,
return tfm;
}
else {
else
#endif /* __OBJECT_MOTION__ */
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
if (itfm)
*itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
@ -99,7 +103,6 @@ ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals kg,
return tfm;
}
}
#endif
/* Get transform matrix for shading point. */

View File

@ -306,9 +306,9 @@ ccl_device_forceinline float4 primitive_motion_vector(KernelGlobals kg,
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (is_curve_or_point) {
motion_pre = float4_to_float3(curve_attribute_float4(kg, sd, desc, NULL, NULL));
motion_pre = float4_to_float3(primitive_surface_attribute_float4(kg, sd, desc, NULL, NULL));
desc.offset += numverts;
motion_post = float4_to_float3(curve_attribute_float4(kg, sd, desc, NULL, NULL));
motion_post = float4_to_float3(primitive_surface_attribute_float4(kg, sd, desc, NULL, NULL));
/* Curve */
if ((sd->object_flag & SD_OBJECT_HAS_VERTEX_MOTION) == 0) {

View File

@ -14,17 +14,17 @@ CCL_NAMESPACE_BEGIN
/* ShaderData setup from incoming ray */
#ifdef __OBJECT_MOTION__
ccl_device void shader_setup_object_transforms(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
float time)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
sd->ob_tfm_motion = object_fetch_transform_motion(kg, sd->object, time);
sd->ob_itfm_motion = transform_inverse(sd->ob_tfm_motion);
}
}
#endif
}
/* TODO: break this up if it helps reduce register pressure to load data from
* global memory as we write it to shader-data. */
@ -273,6 +273,7 @@ ccl_device void shader_setup_from_displace(KernelGlobals kg,
/* ShaderData setup for point on curve. */
#ifdef __HAIR__
ccl_device void shader_setup_from_curve(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
int object,
@ -296,9 +297,9 @@ ccl_device void shader_setup_from_curve(KernelGlobals kg,
/* Object */
sd->object = object;
sd->object_flag = kernel_data_fetch(object_flag, sd->object);
#ifdef __OBJECT_MOTION__
# ifdef __OBJECT_MOTION__
shader_setup_object_transforms(kg, sd, sd->time);
#endif
# endif
/* Get control points. */
KernelCurve kcurve = kernel_data_fetch(curves, prim);
@ -317,34 +318,35 @@ ccl_device void shader_setup_from_curve(KernelGlobals kg,
/* Interpolate position and tangent. */
sd->P = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u));
#ifdef __DPDU__
# ifdef __DPDU__
sd->dPdu = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u));
#endif
# endif
/* Transform into world space */
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform_auto(kg, sd, &sd->P);
#ifdef __DPDU__
# ifdef __DPDU__
object_dir_transform_auto(kg, sd, &sd->dPdu);
#endif
# endif
}
/* No view direction, normals or bitangent. */
sd->wi = zero_float3();
sd->N = zero_float3();
sd->Ng = zero_float3();
#ifdef __DPDU__
# ifdef __DPDU__
sd->dPdv = zero_float3();
#endif
# endif
/* No ray differentials currently. */
#ifdef __RAY_DIFFERENTIALS__
# ifdef __RAY_DIFFERENTIALS__
sd->dP = differential_zero_compact();
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
#endif
# endif
}
#endif /* __HAIR__ */
/* ShaderData setup from ray into background */

View File

@ -328,7 +328,9 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index);
}
#ifdef __SHADOW_CATCHER__
integrator_split_shadow_catcher(kg, state, &isect, render_buffer);
#endif
}
return true;

View File

@ -43,9 +43,11 @@ ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg,
if (shader_flags & (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
}
#ifdef __VOLUME__
else if (!integrator_state_volume_stack_is_empty(kg, state)) {
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_AFTER_VOLUME;
}
#endif
else {
return true;
}
@ -73,10 +75,12 @@ ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg,
/* Mark path to be terminated right after shader evaluation on the surface. */
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE;
}
#ifdef __VOLUME__
else if (!integrator_state_volume_stack_is_empty(kg, state)) {
/* TODO: only do this for emissive volumes. */
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_IN_NEXT_VOLUME;
}
#endif
else {
return true;
}
@ -127,12 +131,14 @@ ccl_device_forceinline void integrator_split_shadow_catcher(
return;
}
# ifdef __VOLUME__
if (!integrator_state_volume_stack_is_empty(kg, state)) {
/* Volume stack is not empty. Re-init the volume stack to exclude any non-shadow catcher
* objects from it, and then continue shading volume and shadow catcher surface after. */
integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK);
return;
}
# endif
/* Continue with shading shadow catcher surface. */
const int shader = intersection_get_shader(kg, isect);
@ -189,6 +195,7 @@ template<DeviceKernel current_kernel>
ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catcher_background(
KernelGlobals kg, IntegratorState state)
{
# ifdef __VOLUME__
/* Same logic as integrator_split_shadow_catcher, but using NEXT instead of INIT. */
if (!integrator_state_volume_stack_is_empty(kg, state)) {
/* Volume stack is not empty. Re-init the volume stack to exclude any non-shadow catcher
@ -197,6 +204,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catche
kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK);
return;
}
# endif
/* Continue with shading shadow catcher surface. */
integrator_intersect_next_kernel_after_shadow_catcher_volume<current_kernel>(kg, state);

View File

@ -10,6 +10,7 @@
CCL_NAMESPACE_BEGIN
#ifdef __VOLUME__
ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
IntegratorState state,
const float3 from_P,
@ -37,7 +38,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint32_t visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, PATH_RAY_ALL_VISIBILITY);
#ifdef __VOLUME_RECORD_ALL__
# ifdef __VOLUME_RECORD_ALL__
Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1];
uint num_hits = scene_intersect_volume(kg, &volume_ray, hits, 2 * volume_stack_size, visibility);
if (num_hits > 0) {
@ -54,7 +55,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
volume_stack_enter_exit(kg, state, stack_sd);
}
}
#else
# else
Intersection isect;
int step = 0;
while (step < 2 * volume_stack_size &&
@ -71,7 +72,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
volume_ray.self.prim = isect.prim;
++step;
}
#endif
# endif
}
ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState state)
@ -114,7 +115,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
/* Store to avoid global fetches on every intersection step. */
const uint volume_stack_size = kernel_data.volume_stack_size;
#ifdef __VOLUME_RECORD_ALL__
# ifdef __VOLUME_RECORD_ALL__
Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1];
uint num_hits = scene_intersect_volume(kg, &volume_ray, hits, 2 * volume_stack_size, visibility);
if (num_hits > 0) {
@ -157,7 +158,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
}
}
}
#else
# else
/* CUDA does not support definition of a variable size arrays, so use the maximum possible. */
int enclosed_volumes[MAX_VOLUME_STACK_SIZE];
int step = 0;
@ -211,7 +212,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
volume_ray.self.prim = isect.prim;
++step;
}
#endif
# endif
/* Write terminator. */
const VolumeStack new_entry = {OBJECT_NONE, SHADER_NONE};
@ -222,12 +223,15 @@ ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorSt
{
integrator_volume_stack_init(kg, state);
# ifdef __SHADOW_CATCHER__
if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) {
/* Volume stack re-init for shadow catcher, continue with shading of hit. */
integrator_intersect_next_kernel_after_shadow_catcher_volume<
DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK>(kg, state);
}
else {
else
# endif
{
/* Volume stack init for camera rays, continue with intersection of camera ray. */
integrator_path_next(kg,
state,
@ -235,5 +239,6 @@ ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorSt
DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST);
}
}
#endif /* __VOLUME__ */
CCL_NAMESPACE_END

View File

@ -598,7 +598,8 @@ mnee_sample_bsdf_dh(ClosureType type, float alpha_x, float alpha_y, float sample
* We assume here that the pdf (in half-vector measure) is the same as
* the one calculation when sampling the microfacet normals from the
* specular chain above: this allows us to simplify the bsdf weight */
ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(ccl_private ShaderClosure *closure,
ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(KernelGlobals kg,
ccl_private ShaderClosure *closure,
float3 wi,
float3 wo)
{
@ -623,7 +624,7 @@ ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(ccl_private ShaderCl
}
Spectrum reflectance, transmittance;
microfacet_fresnel(bsdf, cosHI, nullptr, &reflectance, &transmittance);
microfacet_fresnel(kg, bsdf, cosHI, nullptr, &reflectance, &transmittance);
/*
* bsdf_do = (1 - F) * D_do * G * |h.wi| / (n.wi * n.wo)
@ -895,7 +896,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
/* Evaluate product term inside eq.6 at solution interface. vi
* divided by corresponding sampled pdf:
* fr(vi)_do / pdf_dh(vi) x |do/dh| x |n.wo / n.h| */
Spectrum bsdf_contribution = mnee_eval_bsdf_contribution(v.bsdf, wi, wo);
Spectrum bsdf_contribution = mnee_eval_bsdf_contribution(kg, v.bsdf, wi, wo);
bsdf_eval_mul(throughput, bsdf_contribution);
}

View File

@ -206,8 +206,10 @@ integrate_direct_light_shadow_init_common(KernelGlobals kg,
IntegratorShadowState shadow_state = integrator_shadow_path_init(
kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, false);
#ifdef __VOLUME__
/* Copy volume stack and enter/exit volume. */
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
#endif
/* Write shadow ray and associated state to global memory. */
integrator_state_write_shadow_ray(shadow_state, ray);
@ -644,8 +646,10 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
IntegratorShadowState shadow_state = integrator_shadow_path_init(
kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, true);
# ifdef __VOLUME__
/* Copy volume stack and enter/exit volume. */
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
# endif
/* Write shadow ray and associated state to global memory. */
integrator_state_write_shadow_ray(shadow_state, &ray);

View File

@ -115,6 +115,7 @@ ccl_device_forceinline void integrator_state_read_isect(
isect->t = INTEGRATOR_STATE(state, isect, t);
}
#ifdef __VOLUME__
ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(ConstIntegratorState state,
int i)
{
@ -139,34 +140,6 @@ ccl_device_forceinline bool integrator_state_volume_stack_is_empty(KernelGlobals
true;
}
/* Shadow Intersection */
ccl_device_forceinline void integrator_state_write_shadow_isect(
IntegratorShadowState state,
ccl_private const Intersection *ccl_restrict isect,
const int index)
{
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, t) = isect->t;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, u) = isect->u;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, v) = isect->v;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, object) = isect->object;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, prim) = isect->prim;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, type) = isect->type;
}
ccl_device_forceinline void integrator_state_read_shadow_isect(
ConstIntegratorShadowState state,
ccl_private Intersection *ccl_restrict isect,
const int index)
{
isect->prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, prim);
isect->object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, object);
isect->type = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, type);
isect->u = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, u);
isect->v = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, v);
isect->t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, t);
}
ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(
KernelGlobals kg, IntegratorShadowState shadow_state, ConstIntegratorState state)
{
@ -228,6 +201,36 @@ ccl_device_forceinline void integrator_state_write_shadow_volume_stack(Integrato
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, i, shader) = entry.shader;
}
#endif /* __VOLUME__*/
/* Shadow Intersection */
ccl_device_forceinline void integrator_state_write_shadow_isect(
IntegratorShadowState state,
ccl_private const Intersection *ccl_restrict isect,
const int index)
{
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, t) = isect->t;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, u) = isect->u;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, v) = isect->v;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, object) = isect->object;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, prim) = isect->prim;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, type) = isect->type;
}
ccl_device_forceinline void integrator_state_read_shadow_isect(
ConstIntegratorShadowState state,
ccl_private Intersection *ccl_restrict isect,
const int index)
{
isect->prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, prim);
isect->object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, object);
isect->type = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, type);
isect->u = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, u);
isect->v = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, v);
isect->t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, t);
}
#if defined(__KERNEL_GPU__)
ccl_device_inline void integrator_state_copy_only(KernelGlobals kg,
ConstIntegratorState to_state,

View File

@ -6,13 +6,15 @@
CCL_NAMESPACE_BEGIN
#ifdef __VOLUME__
/* Volumetric read/write lambda functions - default implementations */
#ifndef VOLUME_READ_LAMBDA
# define VOLUME_READ_LAMBDA(function_call) \
auto volume_read_lambda_pass = [=](const int i) { return function_call; };
# define VOLUME_WRITE_LAMBDA(function_call) \
auto volume_write_lambda_pass = [=](const int i, VolumeStack entry) { function_call; };
#endif
# ifndef VOLUME_READ_LAMBDA
# define VOLUME_READ_LAMBDA(function_call) \
auto volume_read_lambda_pass = [=](const int i) { return function_call; };
# define VOLUME_WRITE_LAMBDA(function_call) \
auto volume_write_lambda_pass = [=](const int i, VolumeStack entry) { function_call; };
# endif
/* Volume Stack
*
@ -210,4 +212,6 @@ ccl_device VolumeSampleMethod volume_stack_sample_method(KernelGlobals kg, Integ
return method;
}
#endif /* __VOLUME__*/
CCL_NAMESPACE_END

View File

@ -274,6 +274,8 @@ ccl_device void osl_closure_dielectric_bsdf_setup(KernelGlobals kg,
fresnel->reflection_tint = rgb_to_spectrum(closure->reflection_tint);
fresnel->transmission_tint = rgb_to_spectrum(closure->transmission_tint);
fresnel->thin_film.thickness = 0.0f;
fresnel->thin_film.ior = 0.0f;
bsdf_microfacet_setup_fresnel_dielectric_tint(kg, bsdf, sd, fresnel, preserve_energy);
if (layer_albedo != NULL) {
@ -423,6 +425,8 @@ ccl_device void osl_closure_generalized_schlick_bsdf_setup(
fresnel->f0 = rgb_to_spectrum(closure->f0);
fresnel->f90 = rgb_to_spectrum(closure->f90);
fresnel->exponent = closure->exponent;
fresnel->thin_film.thickness = 0.0f;
fresnel->thin_film.ior = 0.0f;
bsdf_microfacet_setup_fresnel_generalized_schlick(kg, bsdf, sd, fresnel, preserve_energy);
if (layer_albedo != NULL) {

View File

@ -83,8 +83,8 @@ ccl_device
uint specular_ior_level_offset, roughness_offset, specular_tint_offset, anisotropic_offset,
sheen_weight_offset, sheen_tint_offset, sheen_roughness_offset, coat_weight_offset,
coat_roughness_offset, coat_ior_offset, eta_offset, transmission_weight_offset,
anisotropic_rotation_offset, coat_tint_offset, coat_normal_offset, dummy, alpha_offset,
emission_strength_offset, emission_offset, unused;
anisotropic_rotation_offset, coat_tint_offset, coat_normal_offset, alpha_offset,
emission_strength_offset, emission_offset, thinfilm_thickness_offset, unused;
uint4 data_node2 = read_node(kg, &offset);
float3 T = stack_load_float3(stack, data_node.y);
@ -147,21 +147,24 @@ ccl_device
// get the subsurface scattering data
uint4 data_subsurf = read_node(kg, &offset);
uint4 data_alpha_emission = read_node(kg, &offset);
svm_unpack_node_uchar4(data_alpha_emission.x,
uint4 data_alpha_emission_thin = read_node(kg, &offset);
svm_unpack_node_uchar4(data_alpha_emission_thin.x,
&alpha_offset,
&emission_strength_offset,
&emission_offset,
&dummy);
&thinfilm_thickness_offset);
float alpha = stack_valid(alpha_offset) ? stack_load_float(stack, alpha_offset) :
__uint_as_float(data_alpha_emission.y);
__uint_as_float(data_alpha_emission_thin.y);
alpha = saturatef(alpha);
float emission_strength = stack_valid(emission_strength_offset) ?
stack_load_float(stack, emission_strength_offset) :
__uint_as_float(data_alpha_emission.z);
__uint_as_float(data_alpha_emission_thin.z);
float3 emission = stack_load_float3(stack, emission_offset) * emission_strength;
float thinfilm_thickness = fmaxf(stack_load_float(stack, thinfilm_thickness_offset), 1e-5f);
float thinfilm_ior = fmaxf(stack_load_float(stack, data_alpha_emission_thin.w), 1e-5f);
Spectrum weight = closure_weight * mix_weight;
float alpha_x = sqr(roughness), alpha_y = sqr(roughness);
@ -323,6 +326,9 @@ ccl_device
fresnel->transmission_tint = refractive_caustics ?
sqrt(rgb_to_spectrum(clamped_base_color)) :
zero_spectrum();
fresnel->thin_film.thickness = thinfilm_thickness;
fresnel->thin_film.ior = (sd->flag & SD_BACKFACING) ? thinfilm_ior / ior :
thinfilm_ior;
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
@ -346,7 +352,7 @@ ccl_device
}
/* Specular component */
if (reflective_caustics && eta != 1.0f) {
if (reflective_caustics && (eta != 1.0f || thinfilm_thickness > 0.1f)) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
@ -366,6 +372,8 @@ ccl_device
fresnel->exponent = -eta;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = zero_spectrum();
fresnel->thin_film.thickness = thinfilm_thickness;
fresnel->thin_film.ior = thinfilm_ior;
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
@ -458,10 +466,6 @@ ccl_device
Spectrum weight = closure_weight * mix_weight;
float3 position = stack_load_float3(stack, data_node.y);
float3 direction = stack_load_float3(stack, data_node.z);
if (is_zero(direction)) {
direction = -sd->wi;
}
bsdf_ray_portal_setup(sd, weight, path_flag, position, direction);
break;
}
@ -601,6 +605,8 @@ ccl_device
fresnel->reflection_tint = reflective_caustics ? rgb_to_spectrum(color) : zero_spectrum();
fresnel->transmission_tint = refractive_caustics ? rgb_to_spectrum(color) :
zero_spectrum();
fresnel->thin_film.thickness = 0.0f;
fresnel->thin_film.ior = 0.0f;
/* setup bsdf */
if (type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID) {

View File

@ -37,12 +37,14 @@ ccl_device_noinline int svm_node_tex_voxel(
r = kernel_tex_image_interp_3d(kg, id, co, INTERPOLATION_NONE);
}
else if (space != NODE_TEX_VOXEL_SPACE_OBJECT) {
else
#endif /* __VOLUME__ */
if (space != NODE_TEX_VOXEL_SPACE_OBJECT)
{
read_node_float(kg, &offset);
read_node_float(kg, &offset);
read_node_float(kg, &offset);
}
#endif
if (stack_valid(density_out_offset)) {
stack_store_float(stack, density_out_offset, r.w);

View File

@ -62,6 +62,109 @@ CCL_NAMESPACE_BEGIN
# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU
#endif
/* Kernel Features */
/* Shader nodes. */
#define KERNEL_FEATURE_NODE_BSDF (1U << 0U)
#define KERNEL_FEATURE_NODE_EMISSION (1U << 1U)
#define KERNEL_FEATURE_NODE_VOLUME (1U << 2U)
#define KERNEL_FEATURE_NODE_BUMP (1U << 3U)
#define KERNEL_FEATURE_NODE_BUMP_STATE (1U << 4U)
#define KERNEL_FEATURE_NODE_VORONOI_EXTRA (1U << 5U)
#define KERNEL_FEATURE_NODE_RAYTRACE (1U << 6U)
#define KERNEL_FEATURE_NODE_AOV (1U << 7U)
#define KERNEL_FEATURE_NODE_LIGHT_PATH (1U << 8U)
#define KERNEL_FEATURE_NODE_PRINCIPLED_HAIR (1U << 9U)
/* Use path tracing kernels. */
#define KERNEL_FEATURE_PATH_TRACING (1U << 10U)
/* BVH/sampling kernel features. */
#define KERNEL_FEATURE_POINTCLOUD (1U << 11U)
#define KERNEL_FEATURE_HAIR (1U << 12U)
#define KERNEL_FEATURE_HAIR_THICK (1U << 13U)
#define KERNEL_FEATURE_OBJECT_MOTION (1U << 14U)
/* Denotes whether baking functionality is needed. */
#define KERNEL_FEATURE_BAKING (1U << 15U)
/* Use subsurface scattering materials. */
#define KERNEL_FEATURE_SUBSURFACE (1U << 16U)
/* Use volume materials. */
#define KERNEL_FEATURE_VOLUME (1U << 17U)
/* Use OpenSubdiv patch evaluation */
#define KERNEL_FEATURE_PATCH_EVALUATION (1U << 18U)
/* Use Transparent shadows */
#define KERNEL_FEATURE_TRANSPARENT (1U << 19U)
/* Use shadow catcher. */
#define KERNEL_FEATURE_SHADOW_CATCHER (1U << 20U)
/* Light render passes. */
#define KERNEL_FEATURE_LIGHT_PASSES (1U << 21U)
/* AO. */
#define KERNEL_FEATURE_AO_PASS (1U << 22U)
#define KERNEL_FEATURE_AO_ADDITIVE (1U << 23U)
#define KERNEL_FEATURE_AO (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE)
/* MNEE. */
#define KERNEL_FEATURE_MNEE (1U << 24U)
/* Path guiding. */
#define KERNEL_FEATURE_PATH_GUIDING (1U << 25U)
/* OSL. */
#define KERNEL_FEATURE_OSL (1U << 26U)
/* Light and shadow linking. */
#define KERNEL_FEATURE_LIGHT_LINKING (1U << 27U)
#define KERNEL_FEATURE_SHADOW_LINKING (1U << 28U)
/* Use denoising kernels and output denoising passes. */
#define KERNEL_FEATURE_DENOISING (1U << 29U)
/* Light tree. */
#define KERNEL_FEATURE_LIGHT_TREE (1U << 30U)
/* Shader node feature mask, to specialize shader evaluation for kernels. */
#define KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT \
(KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VORONOI_EXTRA | \
KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_SURFACE_BACKGROUND \
(KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT | KERNEL_FEATURE_NODE_AOV)
#define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \
(KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_BUMP | \
KERNEL_FEATURE_NODE_BUMP_STATE | KERNEL_FEATURE_NODE_VORONOI_EXTRA | \
KERNEL_FEATURE_NODE_LIGHT_PATH | KERNEL_FEATURE_NODE_PRINCIPLED_HAIR)
#define KERNEL_FEATURE_NODE_MASK_SURFACE \
(KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE | \
KERNEL_FEATURE_NODE_AOV | KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_VOLUME \
(KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \
KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_DISPLACEMENT \
(KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE)
#define KERNEL_FEATURE_NODE_MASK_BUMP KERNEL_FEATURE_NODE_MASK_DISPLACEMENT
/* Must be constexpr on the CPU to avoid compile errors because the state types
* are different depending on the main, shadow or null path. For GPU we don't have
* C++17 everywhere so need to check it. */
#if __cplusplus < 201703L
# define IF_KERNEL_FEATURE(feature) if ((node_feature_mask & (KERNEL_FEATURE_##feature)) != 0U)
# define IF_KERNEL_NODES_FEATURE(feature) \
if ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U)
#else
# define IF_KERNEL_FEATURE(feature) \
if constexpr ((node_feature_mask & (KERNEL_FEATURE_##feature)) != 0U)
# define IF_KERNEL_NODES_FEATURE(feature) \
if constexpr ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U)
#endif
/* Kernel features */
#define __AO__
#define __CAUSTICS_TRICKS__
@ -122,35 +225,55 @@ CCL_NAMESPACE_BEGIN
# endif
#endif
/* Scene-based selective features compilation. */
/* Scene-based selective features compilation. */
#ifdef __KERNEL_FEATURES__
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_OBJECT_MOTION)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_OBJECT_MOTION)
# undef __OBJECT_MOTION__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_HAIR)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_HAIR)
# undef __HAIR__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_POINTCLOUD)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_POINTCLOUD)
# undef __POINTCLOUD__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_VOLUME)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_VOLUME)
# undef __VOLUME__
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_TRANSPARENT)
# undef __TRANSPARENT_SHADOWS__
# undef __SHADOW_RECORD_ALL__
# endif
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_SUBSURFACE)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_SUBSURFACE)
# undef __SUBSURFACE__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_PATCH_EVALUATION)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_PATCH_EVALUATION)
# undef __PATCH_EVAL__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_TRANSPARENT)
# undef __TRANSPARENT_SHADOWS__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_SHADOW_CATCHER)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_SHADOW_CATCHER)
# undef __SHADOW_CATCHER__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_DENOISING)
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_DENOISING)
# undef __DENOISING_FEATURES__
# endif
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_AO)
# undef __AO__
# endif
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_MNEE)
# undef __MNEE__
# endif
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_PATH_GUIDING)
# undef __PATH_GUIDING__
# endif
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_NODE_PRINCIPLED_HAIR)
# undef __PRINCIPLED_HAIR__
# endif
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_LIGHT_LINKING)
# undef __LIGHT_LINKING__
# endif
# if !(__KERNEL_FEATURES__ & KERNEL_FEATURE_SHADOW_LINKING)
# undef __SHADOW_LINKING__
# endif
#endif
#ifdef WITH_CYCLES_DEBUG_NAN
@ -1687,109 +1810,4 @@ enum {
DEVICE_KERNEL_INTEGRATOR_NUM = DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL + 1,
};
/* Kernel Features */
enum KernelFeatureFlag : uint32_t {
/* Shader nodes. */
KERNEL_FEATURE_NODE_BSDF = (1U << 0U),
KERNEL_FEATURE_NODE_EMISSION = (1U << 1U),
KERNEL_FEATURE_NODE_VOLUME = (1U << 2U),
KERNEL_FEATURE_NODE_BUMP = (1U << 3U),
KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 4U),
KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 5U),
KERNEL_FEATURE_NODE_RAYTRACE = (1U << 6U),
KERNEL_FEATURE_NODE_AOV = (1U << 7U),
KERNEL_FEATURE_NODE_LIGHT_PATH = (1U << 8U),
KERNEL_FEATURE_NODE_PRINCIPLED_HAIR = (1U << 9U),
/* Use path tracing kernels. */
KERNEL_FEATURE_PATH_TRACING = (1U << 10U),
/* BVH/sampling kernel features. */
KERNEL_FEATURE_POINTCLOUD = (1U << 11U),
KERNEL_FEATURE_HAIR = (1U << 12U),
KERNEL_FEATURE_HAIR_THICK = (1U << 13U),
KERNEL_FEATURE_OBJECT_MOTION = (1U << 14U),
/* Denotes whether baking functionality is needed. */
KERNEL_FEATURE_BAKING = (1U << 15U),
/* Use subsurface scattering materials. */
KERNEL_FEATURE_SUBSURFACE = (1U << 16U),
/* Use volume materials. */
KERNEL_FEATURE_VOLUME = (1U << 17U),
/* Use OpenSubdiv patch evaluation */
KERNEL_FEATURE_PATCH_EVALUATION = (1U << 18U),
/* Use Transparent shadows */
KERNEL_FEATURE_TRANSPARENT = (1U << 19U),
/* Use shadow catcher. */
KERNEL_FEATURE_SHADOW_CATCHER = (1U << 20U),
/* Light render passes. */
KERNEL_FEATURE_LIGHT_PASSES = (1U << 21U),
/* AO. */
KERNEL_FEATURE_AO_PASS = (1U << 22U),
KERNEL_FEATURE_AO_ADDITIVE = (1U << 23U),
KERNEL_FEATURE_AO = (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE),
/* MNEE. */
KERNEL_FEATURE_MNEE = (1U << 24U),
/* Path guiding. */
KERNEL_FEATURE_PATH_GUIDING = (1U << 25U),
/* OSL. */
KERNEL_FEATURE_OSL = (1U << 26U),
/* Light and shadow linking. */
KERNEL_FEATURE_LIGHT_LINKING = (1U << 27U),
KERNEL_FEATURE_SHADOW_LINKING = (1U << 28U),
/* Use denoising kernels and output denoising passes. */
KERNEL_FEATURE_DENOISING = (1U << 29U),
/* Light tree. */
KERNEL_FEATURE_LIGHT_TREE = (1U << 30U),
};
/* Shader node feature mask, to specialize shader evaluation for kernels. */
#define KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT \
(KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VORONOI_EXTRA | \
KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_SURFACE_BACKGROUND \
(KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT | KERNEL_FEATURE_NODE_AOV)
#define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \
(KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_BUMP | \
KERNEL_FEATURE_NODE_BUMP_STATE | KERNEL_FEATURE_NODE_VORONOI_EXTRA | \
KERNEL_FEATURE_NODE_LIGHT_PATH | KERNEL_FEATURE_NODE_PRINCIPLED_HAIR)
#define KERNEL_FEATURE_NODE_MASK_SURFACE \
(KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE | \
KERNEL_FEATURE_NODE_AOV | KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_VOLUME \
(KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \
KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_DISPLACEMENT \
(KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE)
#define KERNEL_FEATURE_NODE_MASK_BUMP KERNEL_FEATURE_NODE_MASK_DISPLACEMENT
/* Must be constexpr on the CPU to avoid compile errors because the state types
* are different depending on the main, shadow or null path. For GPU we don't have
* C++17 everywhere so need to check it. */
#if __cplusplus < 201703L
# define IF_KERNEL_FEATURE(feature) if ((node_feature_mask & (KERNEL_FEATURE_##feature)) != 0U)
# define IF_KERNEL_NODES_FEATURE(feature) \
if ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U)
#else
# define IF_KERNEL_FEATURE(feature) \
if constexpr ((node_feature_mask & (KERNEL_FEATURE_##feature)) != 0U)
# define IF_KERNEL_NODES_FEATURE(feature) \
if constexpr ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U)
#endif
CCL_NAMESPACE_END

View File

@ -642,6 +642,7 @@ void ShaderManager::device_update_common(Device * /*device*/,
kfilm->xyz_to_g = float3_to_float4(xyz_to_g);
kfilm->xyz_to_b = float3_to_float4(xyz_to_b);
kfilm->rgb_to_y = float3_to_float4(rgb_to_y);
kfilm->white_xyz = float3_to_float4(white_xyz);
kfilm->rec709_to_r = float3_to_float4(rec709_to_r);
kfilm->rec709_to_g = float3_to_float4(rec709_to_g);
kfilm->rec709_to_b = float3_to_float4(rec709_to_b);
@ -894,6 +895,7 @@ void ShaderManager::init_xyz_transforms()
xyz_to_g = float4_to_float3(xyz_to_rec709.y);
xyz_to_b = float4_to_float3(xyz_to_rec709.z);
rgb_to_y = make_float3(0.2126729f, 0.7151522f, 0.0721750f);
white_xyz = make_float3(0.95047f, 1.0f, 1.08883f);
rec709_to_r = make_float3(1.0f, 0.0f, 0.0f);
rec709_to_g = make_float3(0.0f, 1.0f, 0.0f);
@ -950,6 +952,7 @@ void ShaderManager::init_xyz_transforms()
const Transform rgb_to_xyz = transform_inverse(xyz_to_rgb);
rgb_to_y = float4_to_float3(rgb_to_xyz.y);
white_xyz = transform_direction(&rgb_to_xyz, one_float3());
const Transform rec709_to_rgb = xyz_to_rgb * transform_inverse(xyz_to_rec709);
rec709_to_r = float4_to_float3(rec709_to_rgb.x);

View File

@ -250,6 +250,7 @@ class ShaderManager {
float3 xyz_to_g;
float3 xyz_to_b;
float3 rgb_to_y;
float3 white_xyz;
float3 rec709_to_r;
float3 rec709_to_g;
float3 rec709_to_b;

View File

@ -2661,6 +2661,9 @@ NODE_DEFINE(PrincipledBsdfNode)
SOCKET_IN_COLOR(emission_color, "Emission Color", one_float3());
SOCKET_IN_FLOAT(emission_strength, "Emission Strength", 0.0f);
SOCKET_IN_FLOAT(thin_film_thickness, "Thin Film Thickness", 0.0f);
SOCKET_IN_FLOAT(thin_film_ior, "Thin Film IOR", 1.3f);
SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL);
SOCKET_OUT_CLOSURE(BSDF, "BSDF");
@ -2762,6 +2765,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler)
int alpha_offset = compiler.stack_assign_if_linked(alpha_in);
int emission_strength_offset = compiler.stack_assign_if_linked(emission_strength_in);
int emission_color_offset = compiler.stack_assign(input("Emission Color"));
int thin_film_thickness_offset = compiler.stack_assign(input("Thin Film Thickness"));
int thin_film_ior_offset = compiler.stack_assign(input("Thin Film IOR"));
compiler.add_node(
NODE_CLOSURE_BSDF,
@ -2800,12 +2805,13 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler)
subsurface_scale_offset,
subsurface_anisotropy_offset);
compiler.add_node(
compiler.encode_uchar4(
alpha_offset, emission_strength_offset, emission_color_offset, SVM_STACK_INVALID),
__float_as_int(get_float(alpha_in->socket_type)),
__float_as_int(get_float(emission_strength_in->socket_type)),
SVM_STACK_INVALID);
compiler.add_node(compiler.encode_uchar4(alpha_offset,
emission_strength_offset,
emission_color_offset,
thin_film_thickness_offset),
__float_as_int(get_float(alpha_in->socket_type)),
__float_as_int(get_float(emission_strength_in->socket_type)),
thin_film_ior_offset);
}
void PrincipledBsdfNode::compile(OSLCompiler &compiler)

View File

@ -536,6 +536,8 @@ class PrincipledBsdfNode : public BsdfBaseNode {
NODE_SOCKET_API(float3, emission_color)
NODE_SOCKET_API(float, emission_strength)
NODE_SOCKET_API(float, surface_mix_weight)
NODE_SOCKET_API(float, thin_film_thickness)
NODE_SOCKET_API(float, thin_film_ior)
public:
void attributes(Shader *shader, AttributeRequestSet *attributes);

View File

@ -506,7 +506,8 @@ static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid,
openvdb::math::Extrema extrema;
openvdb::Vec3d voxel_size;
/* External .vdb files have a vec3 type for velocity, but the Blender exporter creates a vec4. */
/* External `.vdb` files have a vec3 type for velocity,
* but the Blender exporter creates a vec4. */
if (grid->isType<openvdb::Vec3fGrid>()) {
openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid);
extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());

View File

@ -119,17 +119,26 @@ ccl_device_inline bool is_zero(const float2 a)
return (a.x == 0.0f && a.y == 0.0f);
}
ccl_device_inline float average(const float2 a)
{
return (a.x + a.y) * (1.0f / 2.0f);
}
ccl_device_inline float dot(const float2 a, const float2 b)
{
return a.x * b.x + a.y * b.y;
}
#endif
ccl_device_inline float average(const float2 a)
{
return (a.x + a.y) * (1.0f / 2.0f);
}
ccl_device_inline bool isequal(const float2 a, const float2 b)
{
#if defined(__KERNEL_METAL__)
return all(a == b);
#else
return a == b;
#endif
}
ccl_device_inline float len(const float2 a)
{
return sqrtf(dot(a, a));

View File

@ -371,6 +371,11 @@ ccl_device_inline float3 log(float3 v)
return make_float3(logf(v.x), logf(v.y), logf(v.z));
}
ccl_device_inline float3 cos(float3 v)
{
return make_float3(cosf(v.x), cosf(v.y), cosf(v.z));
}
ccl_device_inline float3 reflect(const float3 incident, const float3 normal)
{
float3 unit_normal = normalize(normal);

View File

@ -475,11 +475,11 @@ extern void GHOST_setNDOFDeadZone(float deadzone);
#endif
/***************************************************************************************
* Drag'n'drop operations
* Drag & drop operations
***************************************************************************************/
/**
* Tells if the ongoing drag'n'drop object can be accepted upon mouse drop
* Tells if the ongoing drag & drop object can be accepted upon mouse drop.
*/
extern void GHOST_setAcceptDragOperation(GHOST_WindowHandle windowhandle, bool can_accept);

View File

@ -139,7 +139,7 @@ class GHOST_IWindow {
virtual void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const = 0;
/**
* Tells if the ongoing drag'n'drop object can be accepted upon mouse drop
* Tells if the ongoing drag & drop object can be accepted upon mouse drop
*/
virtual void setAcceptDragOperation(bool canAccept) = 0;

View File

@ -18,16 +18,16 @@
* The dragging sequence is performed in four phases:
*
* - Start sequence (GHOST_kEventDraggingEntered) that tells
* a drag'n'drop operation has started.
* a drag & drop operation has started.
* Already gives the object data type, and the entering mouse location
*
* - Update mouse position (GHOST_kEventDraggingUpdated) sent upon each mouse move until the
* drag'n'drop operation stops, to give the updated mouse position.
* drag & drop operation stops, to give the updated mouse position.
* Useful to highlight a potential destination, and update the status
* (through GHOST_setAcceptDragOperation) telling if the object can be dropped at the current
* cursor position.
*
* - Abort drag'n'drop sequence (GHOST_kEventDraggingExited)
* - Abort drag & drop sequence (#GHOST_kEventDraggingExited)
* sent when the user moved the mouse outside the window.
*
* - Send the dropped data (GHOST_kEventDraggingDropDone)

View File

@ -140,8 +140,8 @@ class GHOST_SystemCocoa : public GHOST_System {
bool handleOpenDocumentRequest(void *filepathStr);
/**
* Handles a drag'n'drop destination event. Called by GHOST_WindowCocoa window subclass
* \param eventType: The type of drag'n'drop event.
* Handles a drag & drop destination event. Called by GHOST_WindowCocoa window subclass.
* \param eventType: The type of drag & drop event.
* \param draggedObjectType: The type object concerned.
* (currently array of file names, string, TIFF image).
* \param mouseX: x mouse coordinate (in cocoa base window coordinates).

View File

@ -1763,6 +1763,35 @@ static void gwl_registry_entry_update_all(GWL_Display *display, const int interf
/** \name Private Utility Functions
* \{ */
static const char *strchr_or_end(const char *str, const char ch)
{
const char *p = str;
while (!ELEM(*p, ch, '\0')) {
p++;
}
return p;
}
static bool string_elem_split_by_delim(const char *haystack, const char delim, const char *needle)
{
/* Local copy of #BLI_string_elem_split_by_delim (would be a bad level call). */
/* May be zero, returns true when an empty span exists. */
const size_t needle_len = strlen(needle);
const char *p = haystack, *p_next;
while (true) {
p_next = strchr_or_end(p, delim);
if ((size_t(p_next - p) == needle_len) && (memcmp(p, needle, needle_len) == 0)) {
return true;
}
if (*p_next == '\0') {
break;
}
p = p_next + 1;
}
return false;
}
static uint64_t sub_abs_u64(const uint64_t a, const uint64_t b)
{
return a > b ? a - b : b - a;
@ -1794,16 +1823,36 @@ static const char *ghost_wl_locale_from_env_with_default()
return locale;
}
static void ghost_wl_display_report_error_from_code(wl_display *display, const int ecode)
{
GHOST_ASSERT(ecode, "Error not set!");
if (ELEM(ecode, EPIPE, ECONNRESET)) {
fprintf(stderr, "The Wayland connection broke. Did the Wayland compositor die?\n");
return;
}
if (ecode == EPROTO) {
const struct wl_interface *interface = nullptr;
const int ecode_proto = wl_display_get_protocol_error(display, &interface, nullptr);
fprintf(stderr,
"The Wayland connection experienced a protocol error %d in interface: %s\n",
ecode_proto,
interface ? interface->name : "<nil>");
const char *env_debug = "WAYLAND_DEBUG";
if (getenv(env_debug) == nullptr) {
fprintf(stderr, "Run with the environment variable \"%s=1\" for details.\n", env_debug);
}
return;
}
fprintf(stderr, "The Wayland connection experienced a fatal error: %s\n", strerror(ecode));
}
static void ghost_wl_display_report_error(wl_display *display)
{
int ecode = wl_display_get_error(display);
GHOST_ASSERT(ecode, "Error not set!");
if (ELEM(ecode, EPIPE, ECONNRESET)) {
fprintf(stderr, "The Wayland connection broke. Did the Wayland compositor die?\n");
}
else {
fprintf(stderr, "The Wayland connection experienced a fatal error: %s\n", strerror(ecode));
}
ghost_wl_display_report_error_from_code(display, ecode);
/* NOTE(@ideasman42): The application is running,
* however an error closes all windows and most importantly:
@ -1819,6 +1868,16 @@ static void ghost_wl_display_report_error(wl_display *display)
::exit(-1);
}
bool ghost_wl_display_report_error_if_set(wl_display *display)
{
const int ecode = wl_display_get_error(display);
if (ecode == 0) {
return false;
}
ghost_wl_display_report_error_from_code(display, ecode);
return true;
}
#ifdef __GNUC__
static void ghost_wayland_log_handler(const char *msg, va_list arg)
__attribute__((format(printf, 1, 0)));
@ -7128,11 +7187,10 @@ GHOST_SystemWayland::GHOST_SystemWayland(bool background)
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
bool libdecor_required = false;
{
/* This seems to be the most reliable way to check if GNOME is running.
* Ideally it would be possible to check if the compositor supports SSD. */
const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
if (xdg_current_desktop && STREQ(xdg_current_desktop, "GNOME")) {
if (const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP")) {
/* See the free-desktop specifications for details on `XDG_CURRENT_DESKTOP`.
* https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html */
if (string_elem_split_by_delim(xdg_current_desktop, ':', "GNOME")) {
libdecor_required = true;
}
}

View File

@ -35,6 +35,8 @@
class GHOST_WindowWayland;
bool ghost_wl_display_report_error_if_set(wl_display *display);
bool ghost_wl_output_own(const struct wl_output *wl_output);
void ghost_wl_output_tag(struct wl_output *wl_output);
struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output);

View File

@ -264,9 +264,9 @@ class GHOST_SystemWin32 : public GHOST_System {
GHOST_DialogOptions dialog_options) const;
/**
* Creates a drag'n'drop event and pushes it immediately onto the event queue.
* Called by GHOST_DropTargetWin32 class.
* \param eventType: The type of drag'n'drop event
* Creates a drag & drop event and pushes it immediately onto the event queue.
* Called by #GHOST_DropTargetWin32 class.
* \param eventType: The type of drag & drop event
* \param draggedObjectType: The type object concerned
* (currently array of file names, string, ?bitmap)
* \param mouseX: x mouse coordinate (in window coordinates)

View File

@ -246,9 +246,9 @@ class GHOST_SystemX11 : public GHOST_System {
GHOST_DialogOptions dialog_options) const override;
#ifdef WITH_XDND
/**
* Creates a drag'n'drop event and pushes it immediately onto the event queue.
* Called by GHOST_DropTargetX11 class.
* \param eventType: The type of drag'n'drop event.
* Creates a drag & drop event and pushes it immediately onto the event queue.
* Called by #GHOST_DropTargetX11 class.
* \param eventType: The type of drag & drop event.
* \param draggedObjectType: The type object concerned.
* (currently array of file names, string, ?bitmap)
* \param mouseX: x mouse coordinate (in window coordinates).

View File

@ -200,7 +200,7 @@ class GHOST_Window : public GHOST_IWindow {
GHOST_TSuccess getSwapInterval(int &intervalOut) override;
/**
* Tells if the ongoing drag'n'drop object can be accepted upon mouse drop
* Tells if the ongoing drag & drop object can be accepted upon mouse drop.
*/
void setAcceptDragOperation(bool canAccept) override;
@ -401,7 +401,7 @@ class GHOST_Window : public GHOST_IWindow {
/** The presence of progress indicator with the application icon */
bool m_progressBarVisible;
/** The acceptance of the "drop candidate" of the current drag'n'drop operation */
/** The acceptance of the "drop candidate" of the current drag & drop operation. */
bool m_canAcceptDragOperation;
/** Modified state : are there unsaved changes */

View File

@ -172,7 +172,7 @@
return (associatedWindow->isDialog() || !systemCocoa->hasDialogWindow());
}
/* The drag'n'drop dragging destination methods. */
/* The drag & drop dragging destination methods. */
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
NSPoint mouseLocation = [sender draggingLocation];

View File

@ -1653,6 +1653,8 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
window_->ghost_system = system;
window_->ghost_context_type = type;
wl_display *display = system->wl_display_get();
/* NOTE(@ideasman42): The scale set here to avoid flickering on startup.
* When all monitors use the same scale (which is quite common) there aren't any problems.
*
@ -1710,7 +1712,6 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
/* create window decorations */
decor.frame = libdecor_decorate(
system_->libdecor_context_get(), window_->wl.surface, &libdecor_frame_iface, window_);
libdecor_frame_map(window_->libdecor->frame);
libdecor_frame_set_min_content_size(decor.frame, UNPACK2(size_min));
libdecor_frame_set_app_id(decor.frame, xdg_app_id);
@ -1745,6 +1746,14 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
gwl_window_title_set(window_, title);
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
if (use_libdecor) {
/* Postpone mapping the window until after the app-id & title have been set.
* While this doesn't seem to be a requirement, LIBDECOR example code does this. */
libdecor_frame_map(window_->libdecor->frame);
}
#endif
wl_surface_set_user_data(window_->wl.surface, this);
/* NOTE: the method used for XDG & LIBDECOR initialization (using `initial_configure_seen`)
@ -1771,9 +1780,11 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
/* Commit needed to so configure callback runs. */
wl_surface_commit(window_->wl.surface);
while (!decor.initial_configure_seen) {
wl_display_flush(system->wl_display_get());
wl_display_dispatch(system->wl_display_get());
/* Failure exits with an error, simply prevent an eternal loop. */
while (!decor.initial_configure_seen && !ghost_wl_display_report_error_if_set(display)) {
wl_display_flush(display);
wl_display_dispatch(display);
}
}
@ -1898,7 +1909,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
GWL_LibDecor_Window &decor = *window_->libdecor;
/* Additional round-trip is needed to ensure `xdg_toplevel` is set. */
wl_display_roundtrip(system_->wl_display_get());
wl_display_roundtrip(display);
/* NOTE: LIBDECOR requires the window to be created & configured before the state can be set.
* Workaround this by using the underlying `xdg_toplevel` */
@ -1928,11 +1939,12 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
wl_surface_commit(window_->wl.surface);
::close(fd);
}
# endif /* WITH_GHOST_WAYLAND_LIBDECOR */
# endif /* WITH_VULKAN_BACKEND */
while (!decor.initial_configure_seen) {
wl_display_flush(system->wl_display_get());
wl_display_dispatch(system->wl_display_get());
/* Failure exits with an error, simply prevent an eternal loop. */
while (!decor.initial_configure_seen && !ghost_wl_display_report_error_if_set(display)) {
wl_display_flush(display);
wl_display_dispatch(display);
}
# ifdef WITH_VULKAN_BACKEND
@ -1961,7 +1973,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
* In principle this could be used with XDG too however it causes problems with KDE
* and some WLROOTS based compositors.
*/
wl_display_roundtrip(system_->wl_display_get());
wl_display_roundtrip(display);
}
else
#endif /* WITH_GHOST_WAYLAND_LIBDECOR */

View File

@ -44,7 +44,7 @@
const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
const int GHOST_WindowWin32::s_maxTitleLength = 128;
/* force NVidia Optimus to used dedicated graphics */
/* force NVidia OPTIMUS to used dedicated graphics */
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}

View File

@ -465,7 +465,7 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
# if 0
/* Ideally we'd just create a render target view for the OpenXR swap-chain image texture and
* blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
* this though. At least not with Optimus hardware. See:
* this though. At least not with OPTIMUS hardware. See:
* https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
*/

View File

@ -160,7 +160,7 @@ include_directories(
"../../../source/blender/blenlib"
)
# Needed for math_matrix.c to avoid eigen_capi.h dep.
# Needed for math_matrix_c.cc to avoid eigen_capi.h dep.
add_definitions(-DMATH_STANDALONE)
add_library(bli_lib
@ -170,9 +170,9 @@ add_library(bli_lib
"../../../source/blender/blenlib/intern/string.c"
"../../../source/blender/blenlib/intern/string_utf8.c"
"../../../source/blender/blenlib/intern/listbase.cc"
"../../../source/blender/blenlib/intern/math_color.c"
"../../../source/blender/blenlib/intern/math_color.cc"
"../../../source/blender/blenlib/intern/math_geom.c"
"../../../source/blender/blenlib/intern/math_matrix.c"
"../../../source/blender/blenlib/intern/math_matrix_c.cc"
"../../../source/blender/blenlib/intern/math_rotation.c"
"../../../source/blender/blenlib/intern/math_vector.c"
"../../../source/blender/blenlib/intern/storage.c"

View File

@ -768,7 +768,7 @@ template<typename Mesh> class Mikktspace {
return;
}
/* Todo: Vectorize?
/* TODO: Vectorize?
* Also: Could add special case for flat shading, when all normals are equal half of the fCos
* projections and two of the three tangent projections are unnecessary. */
std::array<float3, 3> n, p;

View File

@ -18,6 +18,7 @@ WAYLAND_DYNLOAD_FN(wl_display_disconnect)
WAYLAND_DYNLOAD_FN(wl_display_dispatch)
WAYLAND_DYNLOAD_FN(wl_display_dispatch_pending)
WAYLAND_DYNLOAD_FN(wl_display_get_fd)
WAYLAND_DYNLOAD_FN(wl_display_get_protocol_error)
WAYLAND_DYNLOAD_FN(wl_display_prepare_read)
WAYLAND_DYNLOAD_FN(wl_display_read_events)
WAYLAND_DYNLOAD_FN(wl_display_cancel_read)
@ -77,6 +78,9 @@ struct WaylandDynload_Client {
int WL_DYN_FN(wl_display_roundtrip)(struct wl_display *display);
int WL_DYN_FN(wl_display_dispatch_pending)(struct wl_display *display);
int WL_DYN_FN(wl_display_get_fd)(struct wl_display *display);
uint32_t WL_DYN_FN(wl_display_get_protocol_error)(struct wl_display *display,
const struct wl_interface **interface,
uint32_t *id);
int WL_DYN_FN(wl_display_prepare_read)(struct wl_display *display);
int WL_DYN_FN(wl_display_read_events)(struct wl_display *display);
void WL_DYN_FN(wl_display_cancel_read)(struct wl_display *display);
@ -118,6 +122,8 @@ struct WaylandDynload_Client {
# define wl_display_dispatch_pending(...) \
(*wayland_dynload_client.wl_display_dispatch)(__VA_ARGS__)
# define wl_display_get_fd(...) (*wayland_dynload_client.wl_display_get_fd)(__VA_ARGS__)
# define wl_display_get_protocol_error(...) \
(*wayland_dynload_client.wl_display_get_protocol_error)(__VA_ARGS__)
# define wl_display_prepare_read(...) \
(*wayland_dynload_client.wl_display_prepare_read)(__VA_ARGS__)
# define wl_display_read_events(...) \

@ -1 +1 @@
Subproject commit dc33a8704935c49dd7a8cf49197fdf42ff52622d
Subproject commit 57ad26e0ccc9a69808ff44b9bb05a18278f4154b

View File

@ -454,6 +454,20 @@ def register_impl(do_register: bool, all_users: bool) -> Optional[str]:
global BLENDER_BIN
global BLENDER_DIR
if BLENDER_ENV:
# File association expects a "portable" build (see `WITH_INSTALL_PORTABLE` CMake option),
# while it's possible support registering a "system" installation, the paths aren't located
# relative to the blender binary and in general it's not needed because system installations
# are used by package managers which can handle file association themselves.
# The Linux builds provided by https://blender.org are portable, register is intended to be used for these.
if __import__("bpy").utils.resource_path('SYSTEM'):
return "System Installation, registration is handled by the package manager"
# While snap builds are portable, the snap system handled file associations.
# Blender is also launched via a wrapper, again, we could support this if it were
# important but we can rely on the snap packaging in this case.
if os.environ.get("SNAP"):
return "Snap Package Installation, registration is handled by the package manager"
if BLENDER_ENV:
# Only use of `bpy`.
BLENDER_BIN = os.path.normpath(__import__("bpy").app.binary_path)

View File

@ -1083,12 +1083,51 @@ def _initialize_extension_repos_post(*_, is_first=False):
modules._is_first = True
def _initialize_extensions_site_packages(*, create=False):
# Add extension site-packages to `sys.path` (if it exists).
# Use for wheels.
import os
import sys
# NOTE: follow the structure of `~/.local/lib/python#.##/site-packages`
# because some wheels contain paths pointing to parent directories,
# referencing `../../../bin` for example - to install binaries into `~/.local/bin`,
# so this can't simply be treated as a module directory unless those files would be excluded
# which may interfere with the wheels functionality.
site_packages = os.path.join(
_bpy.utils.user_resource('EXTENSIONS'),
".local",
"lib",
"python{:d}.{:d}".format(sys.version_info.major, sys.version_info.minor),
"site-packages",
)
if create:
if not os.path.exists(site_packages):
os.makedirs(site_packages)
found = True
else:
found = os.path.exists(site_packages)
if found:
sys.path.append(site_packages)
else:
try:
sys.path.remove(site_packages)
except ValueError:
pass
return site_packages if found else None
def _initialize_extensions_repos_once():
from bpy_extras.extensions.junction_module import JunctionModuleHandle
module_handle = JunctionModuleHandle(_ext_base_pkg_idname)
module_handle.register_module()
_ext_global.module_handle = module_handle
# Ensure extensions wheels can be loaded (when found).
_initialize_extensions_site_packages()
# Setup repositories for the first time.
# Intentionally don't call `_initialize_extension_repos_pre` as this is the first time,
# the previous state is not useful to read.

View File

@ -9,72 +9,42 @@ def url_prefill_from_blender(*, addon_info=None):
import struct
import platform
import urllib.parse
import io
fh = io.StringIO()
query_params = {"type": "bug_report"}
fh.write("**System Information**\n")
fh.write(
"Operating system: {:s} {:d} Bits".format(
platform.platform(),
struct.calcsize("P") * 8,
)
query_params["project"] = "blender-addons" if addon_info else "blender"
query_params["os"] = "{:s} {:d} Bits".format(
platform.platform(),
struct.calcsize("P") * 8,
)
# Windowing Environment (include when dynamically selectable).
# This lets us know if WAYLAND/X11 is in use.
from _bpy import _ghost_backend
ghost_backend = _ghost_backend()
if ghost_backend not in {'NONE', 'DEFAULT'}:
fh.write(", {:s} UI".format(ghost_backend))
query_params["os"] += (", {:s} UI".format(ghost_backend))
del _ghost_backend, ghost_backend
fh.write("\n")
query_params["gpu"] = "{:s} {:s} {:s}".format(
gpu.platform.renderer_get(),
gpu.platform.vendor_get(),
gpu.platform.version_get(),
)
fh.write(
"Graphics card: {:s} {:s} {:s}\n".format(
gpu.platform.renderer_get(),
gpu.platform.vendor_get(),
gpu.platform.version_get(),
)
)
fh.write(
"\n"
"**Blender Version**\n"
)
fh.write(
"Broken: version: {:s}, branch: {:s}, commit date: {:s} {:s}, hash: `{:s}`\n".format(
bpy.app.version_string,
bpy.app.build_branch.decode('utf-8', 'replace'),
bpy.app.build_commit_date.decode('utf-8', 'replace'),
bpy.app.build_commit_time.decode('utf-8', 'replace'),
bpy.app.build_hash.decode('ascii'),
)
)
fh.write(
"Worked: (newest version of Blender that worked as expected)\n"
query_params["broken_version"] = "{:s}, branch: {:s}, commit date: {:s} {:s}, hash: `{:s}`".format(
bpy.app.version_string,
bpy.app.build_branch.decode('utf-8', 'replace'),
bpy.app.build_commit_date.decode('utf-8', 'replace'),
bpy.app.build_commit_time.decode('utf-8', 'replace'),
bpy.app.build_hash.decode('ascii'),
)
if addon_info:
fh.write(
"\n"
"**Addon Information**\n"
)
fh.write(addon_info)
addon_info_lines = addon_info.splitlines()
query_params["addon_name"] = addon_info_lines[0].removeprefix("Name: ")
query_params["addon_author"] = addon_info_lines[1].removeprefix("Author: ")
fh.write(
"\n"
"**Short description of error**\n"
"[Please fill out a short description of the error here]\n"
"\n"
"**Exact steps for others to reproduce the error**\n"
"[Please describe the exact steps needed to reproduce the issue]\n"
"[Based on the default startup or an attached .blend file (as simple as possible)]\n"
"\n"
)
form_number = 2 if addon_info else 1
return (
"https://developer.blender.org/maniphest/task/edit/form/{:d}?description={:s}".format(
form_number,
urllib.parse.quote(fh.getvalue()),
)
)
query_str = urllib.parse.urlencode(query_params)
return f"https://redirect.blender.org/?{query_str}"

View File

@ -284,7 +284,7 @@ def resolve_ncase(path):
dirpath = _os.path.dirname(path)
suffix = path[:0] # "" but ensure byte/str match
if not filename: # dir ends with a slash?
if not filename: # Check if the directory ends with a slash.
if len(dirpath) < len(path):
suffix = path[:len(path) - len(dirpath)]

View File

@ -4649,6 +4649,11 @@ def km_grease_pencil_edit_mode(params):
("grease_pencil.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None),
# Extrude and move selected points
op_tool_optional(
("grease_pencil.extrude_move", {"type": 'E', "value": 'PRESS'}, None),
(op_tool_cycle, "builtin.extrude"), params),
# Active layer
op_menu("GREASE_PENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}),
@ -8053,7 +8058,6 @@ def km_3d_view_tool_sculpt_line_hide(params):
def km_3d_view_tool_sculpt_polyline_hide(params):
# autopep8:off
return (
"3D View Tool: Sculpt, Polyline Hide",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
@ -8064,7 +8068,6 @@ def km_3d_view_tool_sculpt_polyline_hide(params):
{"properties": [("action", 'SHOW')]}),
]},
)
# autopep8: on
def km_3d_view_tool_sculpt_box_mask(params):
@ -8133,6 +8136,16 @@ def km_3d_view_tool_sculpt_lasso_trim(params):
)
def km_3d_view_tool_sculpt_line_trim(params):
return (
"3D View Tool: Sculpt, Line Trim",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("sculpt.trim_line_gesture", params.tool_maybe_tweak_event, None),
]},
)
def km_3d_view_tool_sculpt_line_mask(params):
return (
"3D View Tool: Sculpt, Line Mask",
@ -9061,6 +9074,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_sculpt_lasso_face_set(params),
km_3d_view_tool_sculpt_box_trim(params),
km_3d_view_tool_sculpt_lasso_trim(params),
km_3d_view_tool_sculpt_line_trim(params),
km_3d_view_tool_sculpt_line_mask(params),
km_3d_view_tool_sculpt_line_project(params),
km_3d_view_tool_sculpt_mesh_filter(params),

View File

@ -248,8 +248,8 @@ class NLA_OT_bake(Operator):
description="Which data's transformations to bake",
options={'ENUM_FLAG'},
items=(
('POSE', "Pose", "Bake bones transformations"),
('OBJECT', "Object", "Bake object transformations"),
('POSE', "Pose", "Bake bones transformations"),
('OBJECT', "Object", "Bake object transformations"),
),
default={'POSE'},
)

View File

@ -208,8 +208,10 @@ def find_next(ele_dst, ele_src):
continue
depth_test = tuple(zip(depth_test_a, depth_test_b))
# square so a few high values win over many small ones
diff_test = sum((abs(a[0] - b[0]) ** 2) +
(abs(a[1] - b[1]) ** 2) for a, b in zip(depth_src, depth_test))
diff_test = sum(
(abs(a[0] - b[0]) ** 2) +
(abs(a[1] - b[1]) ** 2) for a, b in zip(depth_src, depth_test)
)
if diff_test > diff_best:
diff_best = diff_test
ele_best = ele_test

View File

@ -440,8 +440,7 @@ class CLIP_OT_constraint_to_fcurve(Operator):
con = x
if not con:
self.report({'ERROR'},
"Motion Tracking constraint to be converted not found")
self.report({'ERROR'}, "Motion Tracking constraint to be converted not found")
return {'CANCELLED'}
@ -452,8 +451,7 @@ class CLIP_OT_constraint_to_fcurve(Operator):
clip = con.clip
if not clip:
self.report({'ERROR'},
"Movie clip to use tracking data from isn't set")
self.report({'ERROR'}, "Movie clip to use tracking data from isn't set")
return {'CANCELLED'}

View File

@ -294,8 +294,10 @@ class WM_OT_blend_strings_utf8_validate(Operator):
for it in getattr(bpy.data, prop.identifier):
changed |= self.validate_strings(it, done_items)
if changed:
self.report({'WARNING'},
"Some strings were fixed, don't forget to save the .blend file to keep those changes")
self.report(
{'WARNING'},
"Some strings were fixed, don't forget to save the .blend file to keep those changes",
)
return {'FINISHED'}

View File

@ -143,9 +143,14 @@ def create_wrapper_group(modifier, old_group):
group.interface.new_socket(data_("Geometry"), in_out='OUTPUT', socket_type='NodeSocketGeometry')
group.is_modifier = True
first_geometry_input = next((item for item in old_group.interface.items_tree if item.item_type == 'SOCKET' and
item.in_out == 'INPUT' and
item.bl_socket_idname == 'NodeSocketGeometry'), None)
first_geometry_input = next(
(
item for item in old_group.interface.items_tree if item.item_type == 'SOCKET' and
item.in_out == 'INPUT' and
item.bl_socket_idname == 'NodeSocketGeometry'
),
None,
)
if first_geometry_input:
group.interface.new_socket(data_("Geometry"), in_out='INPUT', socket_type='NodeSocketGeometry')
group_input_node = group.nodes.new('NodeGroupInput')
@ -180,8 +185,10 @@ def create_wrapper_group(modifier, old_group):
group_node_input.default_value = modifier[identifier]
if first_geometry_input:
group.links.new(group_input_node.outputs[0],
get_socket_with_identifier(group_node.inputs, first_geometry_input.identifier))
group.links.new(
group_input_node.outputs[0],
get_socket_with_identifier(group_node.inputs, first_geometry_input.identifier),
)
# Adjust locations of named attribute input nodes and group input node to make some space.
if input_nodes:
@ -361,242 +368,9 @@ class ZoneOperator:
return True
class NodeOperator:
@classmethod
def get_node(cls, context):
node = context.active_node
if node is None:
return None
if node.bl_idname == cls.node_type:
return node
@classmethod
def poll(cls, context):
space = context.space_data
# Needs active node editor and a tree.
if not space or space.type != 'NODE_EDITOR' or not space.edit_tree or space.edit_tree.library:
return False
node = cls.get_node(context)
if node is None:
return False
return True
class SocketItemAddOperator:
items_name = None
active_index_name = None
default_socket_type = 'GEOMETRY'
def execute(self, context):
node = self.get_node(context)
items = getattr(node, self.items_name)
# Remember index to move the item.
old_active_index = getattr(node, self.active_index_name)
if 0 <= old_active_index < len(items):
old_active_item = items[old_active_index]
dst_index = old_active_index + 1
dst_type = old_active_item.socket_type
dst_name = old_active_item.name
else:
dst_index = len(items)
dst_type = self.default_socket_type
# Empty name so it is based on the type.
dst_name = ""
items.new(dst_type, dst_name)
items.move(len(items) - 1, dst_index)
setattr(node, self.active_index_name, dst_index)
return {'FINISHED'}
class SocketItemRemoveOperator:
items_name = None
active_index_name = None
def execute(self, context):
node = self.get_node(context)
items = getattr(node, self.items_name)
old_active_index = getattr(node, self.active_index_name)
if 0 <= old_active_index < len(items):
items.remove(items[old_active_index])
return {'FINISHED'}
class SocketMoveItemOperator:
items_name = None
active_index_name = None
direction: EnumProperty(
name="Direction",
items=[('UP', "Up", ""), ('DOWN', "Down", "")],
default='UP',
)
def execute(self, context):
node = self.get_node(context)
items = getattr(node, self.items_name)
old_active_index = getattr(node, self.active_index_name)
if self.direction == 'UP' and old_active_index > 0:
items.move(old_active_index, old_active_index - 1)
setattr(node, self.active_index_name, old_active_index - 1)
elif self.direction == 'DOWN' and old_active_index < len(items) - 1:
items.move(old_active_index, old_active_index + 1)
setattr(node, self.active_index_name, old_active_index + 1)
return {'FINISHED'}
class SimulationZoneOperator(ZoneOperator):
input_node_type = 'GeometryNodeSimulationInput'
output_node_type = 'GeometryNodeSimulationOutput'
items_name = "state_items"
active_index_name = "active_index"
class SimulationZoneItemAddOperator(SimulationZoneOperator, SocketItemAddOperator, Operator):
"""Add a state item to the simulation zone"""
bl_idname = "node.simulation_zone_item_add"
bl_label = "Add State Item"
bl_options = {'REGISTER', 'UNDO'}
class SimulationZoneItemRemoveOperator(SimulationZoneOperator, SocketItemRemoveOperator, Operator):
"""Remove a state item from the simulation zone"""
bl_idname = "node.simulation_zone_item_remove"
bl_label = "Remove State Item"
bl_options = {'REGISTER', 'UNDO'}
class SimulationZoneItemMoveOperator(SimulationZoneOperator, SocketMoveItemOperator, Operator):
"""Move a simulation state item up or down in the list"""
bl_idname = "node.simulation_zone_item_move"
bl_label = "Move State Item"
bl_options = {'REGISTER', 'UNDO'}
class RepeatZoneOperator(ZoneOperator):
input_node_type = 'GeometryNodeRepeatInput'
output_node_type = 'GeometryNodeRepeatOutput'
items_name = "repeat_items"
active_index_name = "active_index"
class RepeatZoneItemAddOperator(RepeatZoneOperator, SocketItemAddOperator, Operator):
"""Add a repeat item to the repeat zone"""
bl_idname = "node.repeat_zone_item_add"
bl_label = "Add Repeat Item"
bl_options = {'REGISTER', 'UNDO'}
class RepeatZoneItemRemoveOperator(RepeatZoneOperator, SocketItemRemoveOperator, Operator):
"""Remove a repeat item from the repeat zone"""
bl_idname = "node.repeat_zone_item_remove"
bl_label = "Remove Repeat Item"
bl_options = {'REGISTER', 'UNDO'}
class RepeatZoneItemMoveOperator(RepeatZoneOperator, SocketMoveItemOperator, Operator):
"""Move a repeat item up or down in the list"""
bl_idname = "node.repeat_zone_item_move"
bl_label = "Move Repeat Item"
bl_options = {'REGISTER', 'UNDO'}
class BakeNodeOperator(NodeOperator):
node_type = 'GeometryNodeBake'
items_name = "bake_items"
active_index_name = "active_index"
class BakeNodeItemAddOperator(BakeNodeOperator, SocketItemAddOperator, Operator):
"""Add a bake item to the bake node"""
bl_idname = "node.bake_node_item_add"
bl_label = "Add Bake Item"
bl_options = {'REGISTER', 'UNDO'}
class BakeNodeItemRemoveOperator(BakeNodeOperator, SocketItemRemoveOperator, Operator):
"""Remove a bake item from the bake node"""
bl_idname = "node.bake_node_item_remove"
bl_label = "Remove Bake Item"
bl_options = {'REGISTER', 'UNDO'}
class BakeNodeItemMoveOperator(BakeNodeOperator, SocketMoveItemOperator, Operator):
"""Move a bake item up or down in the list"""
bl_idname = "node.bake_node_item_move"
bl_label = "Move Bake Item"
bl_options = {'REGISTER', 'UNDO'}
def _editable_tree_with_active_node_type(context, node_type):
space = context.space_data
# Needs active node editor and a tree.
if not space or space.type != 'NODE_EDITOR' or not space.edit_tree or space.edit_tree.library:
return False
node = context.active_node
if node is None or node.bl_idname != node_type:
return False
return True
class IndexSwitchItemAddOperator(Operator):
"""Add an item to the index switch"""
bl_idname = "node.index_switch_item_add"
bl_label = "Add Item"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return _editable_tree_with_active_node_type(context, 'GeometryNodeIndexSwitch')
def execute(self, context):
node = context.active_node
node.index_switch_items.new()
return {'FINISHED'}
class IndexSwitchItemRemoveOperator(Operator):
"""Remove an item from the index switch"""
bl_idname = "node.index_switch_item_remove"
bl_label = "Remove Item"
bl_options = {'REGISTER', 'UNDO'}
index: IntProperty(
name="Index",
description="Index of item to remove",
)
@classmethod
def poll(cls, context):
return _editable_tree_with_active_node_type(context, 'GeometryNodeIndexSwitch')
def execute(self, context):
node = context.active_node
items = node.index_switch_items
items.remove(items[self.index])
return {'FINISHED'}
classes = (
NewGeometryNodesModifier,
NewGeometryNodeTreeAssign,
NewGeometryNodeGroupTool,
MoveModifierToNodes,
SimulationZoneItemAddOperator,
SimulationZoneItemRemoveOperator,
SimulationZoneItemMoveOperator,
RepeatZoneItemAddOperator,
RepeatZoneItemRemoveOperator,
RepeatZoneItemMoveOperator,
BakeNodeItemAddOperator,
BakeNodeItemRemoveOperator,
BakeNodeItemMoveOperator,
IndexSwitchItemAddOperator,
IndexSwitchItemRemoveOperator,
)

View File

@ -75,9 +75,11 @@ class EditExternally(Operator):
except BaseException:
import traceback
traceback.print_exc()
self.report({'ERROR'},
"Image editor could not be launched, ensure that "
"the path in User Preferences > File is valid, and Blender has rights to launch it")
self.report(
{'ERROR'},
"Image editor could not be launched, ensure that "
"the path in User Preferences > File is valid, and Blender has rights to launch it",
)
return {'CANCELLED'}

View File

@ -58,8 +58,10 @@ class MeshMirrorUV(Operator):
mirror_lt[co] = i
vmap = {}
for mirror_a, mirror_b in ((mirror_gt, mirror_lt),
(mirror_lt, mirror_gt)):
for mirror_a, mirror_b in (
(mirror_gt, mirror_lt),
(mirror_lt, mirror_gt),
):
for co, i in mirror_a.items():
nco = (-co[0], co[1], co[2])
j = mirror_b.get(nco)

View File

@ -387,61 +387,6 @@ class NODE_OT_interface_item_remove(NodeInterfaceOperator, Operator):
return {'FINISHED'}
class NODE_OT_enum_definition_item_add(Operator):
'''Add an enum item to the definition'''
bl_idname = "node.enum_definition_item_add"
bl_label = "Add Item"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
node = context.active_node
enum_def = node.enum_definition
item = enum_def.enum_items.new(data_("Item"))
enum_def.active_index = enum_def.enum_items[:].index(item)
return {'FINISHED'}
class NODE_OT_enum_definition_item_remove(Operator):
'''Remove the selected enum item from the definition'''
bl_idname = "node.enum_definition_item_remove"
bl_label = "Remove Item"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
node = context.active_node
enum_def = node.enum_definition
item = enum_def.active_item
if item:
enum_def.enum_items.remove(item)
enum_def.active_index = min(max(enum_def.active_index, 0), len(enum_def.enum_items) - 1)
return {'FINISHED'}
class NODE_OT_enum_definition_item_move(Operator):
'''Remove the selected enum item from the definition'''
bl_idname = "node.enum_definition_item_move"
bl_label = "Move Item"
bl_options = {'REGISTER', 'UNDO'}
direction: EnumProperty(
name="Direction",
description="Move up or down",
items=[("UP", "Up", ""), ("DOWN", "Down", "")]
)
def execute(self, context):
node = context.active_node
enum_def = node.enum_definition
index = enum_def.active_index
if self.direction == 'UP':
enum_def.enum_items.move(index, index - 1)
enum_def.active_index = min(max(index - 1, 0), len(enum_def.enum_items) - 1)
else:
enum_def.enum_items.move(index, index + 1)
enum_def.active_index = min(max(index + 1, 0), len(enum_def.enum_items) - 1)
return {'FINISHED'}
class NODE_FH_image_node(FileHandler):
bl_idname = "NODE_FH_image_node"
bl_label = "Image node"
@ -471,7 +416,4 @@ classes = (
NODE_OT_interface_item_duplicate,
NODE_OT_interface_item_remove,
NODE_OT_tree_path_parent,
NODE_OT_enum_definition_item_add,
NODE_OT_enum_definition_item_remove,
NODE_OT_enum_definition_item_move,
)

View File

@ -363,13 +363,17 @@ class ShapeTransfer(Operator):
for ob_other in objects:
if ob_other.type != 'MESH':
self.report({'WARNING'},
rpt_("Skipping '{:s}', not a mesh").format(ob_other.name))
self.report(
{'WARNING'},
rpt_("Skipping '{:s}', not a mesh").format(ob_other.name),
)
continue
me_other = ob_other.data
if len(me_other.vertices) != len(me.vertices):
self.report({'WARNING'},
rpt_("Skipping '{:s}', vertex count differs").format(ob_other.name))
self.report(
{'WARNING'},
rpt_("Skipping '{:s}', vertex count differs").format(ob_other.name),
)
continue
target_normals = me_nos(me_other.vertices)
@ -397,14 +401,15 @@ class ShapeTransfer(Operator):
v_before = idxs[-2]
v = idxs[-1]
for v_after in idxs:
pt = barycentric_transform(orig_shape_coords[v],
orig_coords[v_before],
orig_coords[v],
orig_coords[v_after],
target_coords[v_before],
target_coords[v],
target_coords[v_after],
)
pt = barycentric_transform(
orig_shape_coords[v],
orig_coords[v_before],
orig_coords[v],
orig_coords[v_after],
target_coords[v_before],
target_coords[v],
target_coords[v_after],
)
median_coords[v].append(pt)
v_before = v
v = v_after
@ -467,10 +472,7 @@ class ShapeTransfer(Operator):
if 1: # swap from/to, means we can't copy to many at once.
if len(objects) != 1:
self.report({'ERROR'},
("Expected one other selected "
"mesh object to copy from"))
self.report({'ERROR'}, "Expected one other selected mesh object to copy from")
return {'CANCELLED'}
ob_act, objects = objects[0], [ob_act]
@ -524,44 +526,47 @@ class JoinUVs(Operator):
obj_other.data.tag = False
for obj_other in objects:
if obj_other != obj and obj_other.type == 'MESH':
mesh_other = obj_other.data
if mesh_other != mesh:
if mesh_other.tag is False:
mesh_other.tag = True
if not (obj_other != obj and obj_other.type == 'MESH'):
continue
mesh_other = obj_other.data
if mesh_other == mesh:
continue
if mesh_other.tag is True:
continue
if len(mesh_other.loops) != nbr_loops:
self.report(
{'WARNING'},
rpt_(
"Object: {:s}, Mesh: '{:s}' has {:d} loops (for {:d} faces), expected {:d}\n"
).format(
obj_other.name,
mesh_other.name,
len(mesh_other.loops),
len(mesh_other.polygons),
nbr_loops,
),
)
else:
uv_other = mesh_other.uv_layers.active
if not uv_other:
mesh_other.uv_layers.new()
uv_other = mesh_other.uv_layers.active
if not uv_other:
self.report(
{'ERROR'},
rpt_(
"Could not add a new UV map to object '{:s}' (Mesh '{:s}')\n"
).format(
obj_other.name,
mesh_other.name,
),
)
mesh_other.tag = True
if len(mesh_other.loops) != nbr_loops:
self.report(
{'WARNING'},
rpt_(
"Object: {:s}, Mesh: '{:s}' has {:d} loops (for {:d} faces), expected {:d}"
).format(
obj_other.name,
mesh_other.name,
len(mesh_other.loops),
len(mesh_other.polygons),
nbr_loops,
),
)
else:
uv_other = mesh_other.uv_layers.active
if not uv_other:
mesh_other.uv_layers.new()
uv_other = mesh_other.uv_layers.active
if not uv_other:
self.report(
{'ERROR'},
rpt_(
"Could not add a new UV map to object '{:s}' (Mesh '{:s}')"
).format(
obj_other.name,
mesh_other.name,
),
)
# finally do the copy
uv_other.data.foreach_set("uv", uv_array)
mesh_other.update()
# finally do the copy
uv_other.data.foreach_set("uv", uv_array)
mesh_other.update()
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
@ -821,9 +826,12 @@ class TransformsToDeltasAnim(Operator):
# ensure that this index hasn't occurred before
if fcu.array_index in existingFCurves[dpath]:
# conflict
self.report({'ERROR'},
rpt_("Object {!r} already has {!r} F-Curve(s). "
"Remove these before trying again").format(obj.name, dpath))
self.report(
{'ERROR'},
rpt_(
"Object {!r} already has {!r} F-Curve(s). "
"Remove these before trying again"
).format(obj.name, dpath))
return {'CANCELLED'}
else:
# no conflict here

View File

@ -107,17 +107,21 @@ class QuickFur(ObjectModeOperator, Operator):
node_groups_to_append.add("Hair Curves Noise")
if self.use_frizz:
node_groups_to_append.add("Frizz Hair Curves")
assets_directory = os.path.join(bpy.utils.system_resource('DATAFILES'),
"assets",
"geometry_nodes",
"procedural_hair_node_assets.blend",
"NodeTree")
assets_directory = os.path.join(
bpy.utils.system_resource('DATAFILES'),
"assets",
"geometry_nodes",
"procedural_hair_node_assets.blend",
"NodeTree",
)
for name in node_groups_to_append:
bpy.ops.wm.append(directory=assets_directory,
filename=name,
use_recursive=True,
clear_asset_data=True,
do_reuse_local_id=True)
bpy.ops.wm.append(
directory=assets_directory,
filename=name,
use_recursive=True,
clear_asset_data=True,
do_reuse_local_id=True,
)
generate_group = bpy.data.node_groups["Generate Hair Curves"]
interpolate_group = bpy.data.node_groups["Interpolate Hair Curves"]
radius_group = bpy.data.node_groups["Set Hair Curve Profile"]

View File

@ -550,7 +550,7 @@ class PREFERENCES_OT_theme_install(Operator):
if not self.overwrite:
if os.path.exists(path_dest):
self.report({'WARNING'}, rpt_("File already installed to {!r}\n").format(path_dest))
self.report({'WARNING'}, rpt_("File already installed to {!r}").format(path_dest))
return {'CANCELLED'}
try:
@ -701,7 +701,7 @@ class PREFERENCES_OT_addon_install(Operator):
for f in file_to_extract_root:
path_dest = os.path.join(path_addons, os.path.basename(f))
if os.path.exists(path_dest):
self.report({'WARNING'}, rpt_("File already installed to {!r}\n").format(path_dest))
self.report({'WARNING'}, rpt_("File already installed to {!r}").format(path_dest))
return {'CANCELLED'}
try: # extract the file to "addons"
@ -716,7 +716,7 @@ class PREFERENCES_OT_addon_install(Operator):
if self.overwrite:
_module_filesystem_remove(path_addons, os.path.basename(pyfile))
elif os.path.exists(path_dest):
self.report({'WARNING'}, rpt_("File already installed to {!r}\n").format(path_dest))
self.report({'WARNING'}, rpt_("File already installed to {!r}").format(path_dest))
return {'CANCELLED'}
# if not compressed file just copy into the addon path
@ -946,7 +946,7 @@ class PREFERENCES_OT_app_template_install(Operator):
for f in file_to_extract_root:
path_dest = os.path.join(path_app_templates, os.path.basename(f))
if os.path.exists(path_dest):
self.report({'WARNING'}, rpt_("File already installed to {!r}\n").format(path_dest))
self.report({'WARNING'}, rpt_("File already installed to {!r}").format(path_dest))
return {'CANCELLED'}
try: # extract the file to "bl_app_templates_user"
@ -957,7 +957,7 @@ class PREFERENCES_OT_app_template_install(Operator):
else:
# Only support installing zip-files.
self.report({'WARNING'}, rpt_("Expected a zip-file {!r}\n").format(filepath))
self.report({'WARNING'}, rpt_("Expected a zip-file {!r}").format(filepath))
return {'CANCELLED'}
app_templates_new = set(os.listdir(path_app_templates)) - app_templates_old

View File

@ -174,13 +174,17 @@ def extend(obj, EXTEND_MODE, use_uv_selection):
else:
fac = 1.0
extrapolate_uv(fac,
l_a_uv[3], l_a_uv[0],
l_b_uv[3], l_b_uv[0])
extrapolate_uv(
fac,
l_a_uv[3], l_a_uv[0],
l_b_uv[3], l_b_uv[0],
)
extrapolate_uv(fac,
l_a_uv[2], l_a_uv[1],
l_b_uv[2], l_b_uv[1])
extrapolate_uv(
fac,
l_a_uv[2], l_a_uv[1],
l_b_uv[2], l_b_uv[1],
)
# -------------------------------------------
# Calculate average length per loop if needed.

View File

@ -147,7 +147,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator):
# Don't set the constraint axis since users will expect MMB
# to use the user setting, see: #61637
# "orient_type": 'NORMAL',
# Not a popular choice, too restrictive for retopo.
# Not a popular choice, too restrictive for retopology.
# "constraint_axis": (True, True, False),
"constraint_axis": (False, False, False),
"release_confirm": False,

View File

@ -571,6 +571,7 @@ class NODE_MT_category_GEO_UTILITIES_DEPRECATED(Menu):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeAlignEulerToVector")
node_add_menu.add_node_type(layout, "FunctionNodeRotateEuler")
@ -592,7 +593,7 @@ class NODE_MT_category_GEO_UTILITIES_ROTATION(Menu):
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeAlignEulerToVector")
node_add_menu.add_node_type(layout, "FunctionNodeAlignRotationToVector")
node_add_menu.add_node_type(layout, "FunctionNodeAxisAngleToRotation")
node_add_menu.add_node_type(layout, "FunctionNodeEulerToRotation")
node_add_menu.add_node_type(layout, "FunctionNodeInvertRotation")
@ -611,10 +612,12 @@ class NODE_MT_category_utilities_matrix(Menu):
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeCombineMatrix")
node_add_menu.add_node_type(layout, "FunctionNodeCombineTransform")
node_add_menu.add_node_type(layout, "FunctionNodeInvertMatrix")
node_add_menu.add_node_type(layout, "FunctionNodeMatrixMultiply")
node_add_menu.add_node_type(layout, "FunctionNodeProjectPoint")
node_add_menu.add_node_type(layout, "FunctionNodeSeparateMatrix")
node_add_menu.add_node_type(layout, "FunctionNodeSeparateTransform")
node_add_menu.add_node_type(layout, "FunctionNodeTransformDirection")
node_add_menu.add_node_type(layout, "FunctionNodeTransformPoint")

View File

@ -127,8 +127,10 @@ class DATA_PT_gpencil_layers(DataButtonsPanel, Panel):
layer_rows = 7
col = row.column()
col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
rows=layer_rows, sort_reverse=True, sort_lock=True)
col.template_list(
"GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
rows=layer_rows, sort_reverse=True, sort_lock=True,
)
col = row.column()
sub = col.column(align=True)

View File

@ -46,7 +46,7 @@ class GreasePencil_LayerMaskPanel:
grease_pencil = ob.data
layer = grease_pencil.layers.active
self.layout.prop(layer, "use_masks", text="")
self.layout.prop(layer, "use_masks", text="", toggle=0)
def draw(self, context):
layout = self.layout
@ -63,8 +63,10 @@ class GreasePencil_LayerMaskPanel:
rows = 4
row = layout.row()
col = row.column()
col.template_list("GREASE_PENCIL_UL_masks", "", layer, "mask_layers", layer.mask_layers,
"active_mask_index", rows=rows, sort_lock=True)
col.template_list(
"GREASE_PENCIL_UL_masks", "", layer, "mask_layers", layer.mask_layers,
"active_mask_index", rows=rows, sort_lock=True,
)
col = row.column(align=True)
col.menu("GREASE_PENCIL_MT_layer_mask_add", icon='ADD', text="")
@ -328,10 +330,11 @@ class DATA_PT_grease_pencil_settings(DataButtonsPanel, Panel):
col.prop(grease_pencil, "stroke_depth_order", text="Stroke Depth Order")
class DATA_PT_grease_pencil_custom_props(DataButtonsPanel, PropertyPanel, Panel):
_context_path = "object.data"
_property_type = bpy.types.GreasePencilv3
_has_gpv3 = hasattr(bpy.types, 'GreasePencilv3')
if _has_gpv3:
class DATA_PT_grease_pencil_custom_props(DataButtonsPanel, PropertyPanel, Panel):
_context_path = "object.data"
_property_type = bpy.types.GreasePencilv3
classes = (
GREASE_PENCIL_UL_masks,
@ -345,10 +348,13 @@ classes = (
DATA_PT_grease_pencil_onion_skinning_custom_colors,
DATA_PT_grease_pencil_onion_skinning_display,
DATA_PT_grease_pencil_settings,
DATA_PT_grease_pencil_custom_props,
GREASE_PENCIL_MT_grease_pencil_add_layer_extra,
)
if _has_gpv3:
classes += (DATA_PT_grease_pencil_custom_props,)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:

View File

@ -56,9 +56,14 @@ class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
def draw(self, context):
layout = self.layout
ob_type = context.object.type
geometry_nodes_supported = ob_type in {'MESH', 'CURVE', 'CURVES',
'FONT', 'VOLUME', 'POINTCLOUD', 'GREASEPENCIL'}
ob = context.object
if not ob:
return
ob_type = ob.type
geometry_nodes_supported = ob_type in {
'MESH', 'CURVE', 'CURVES',
'FONT', 'VOLUME', 'POINTCLOUD', 'GREASEPENCIL',
}
if layout.operator_context == 'EXEC_REGION_WIN':
layout.operator_context = 'INVOKE_REGION_WIN'

View File

@ -468,8 +468,10 @@ class AnnotationDataPanel:
layer_rows = 5
else:
layer_rows = 3
col.template_list("GPENCIL_UL_annotation_layer", "", gpd, "layers", gpd.layers, "active_index",
rows=layer_rows, sort_reverse=True, sort_lock=True)
col.template_list(
"GPENCIL_UL_annotation_layer", "", gpd, "layers", gpd.layers, "active_index",
rows=layer_rows, sort_reverse=True, sort_lock=True,
)
col = row.column()

View File

@ -98,8 +98,10 @@ class MASK_PT_layers:
rows = 4 if active_layer else 1
row = layout.row()
row.template_list("MASK_UL_layers", "", mask, "layers",
mask, "active_layer_index", rows=rows)
row.template_list(
"MASK_UL_layers", "", mask, "layers",
mask, "active_layer_index", rows=rows,
)
sub = row.column(align=True)

View File

@ -338,6 +338,11 @@ class EEVEE_NEXT_MATERIAL_PT_settings_surface(MaterialButtonsPanel, Panel):
elif mat.surface_render_method == 'DITHERED':
col.prop(mat, "use_screen_refraction", text="Raytraced Transmission")
col = layout.column()
col.prop(mat, "thickness_mode", text="Thickness")
if mat.surface_render_method == 'DITHERED':
col.prop(mat, "use_thickness_from_shadow", text="From Shadow")
col = layout.column(heading="Light Probe Volume")
col.prop(mat, "lightprobe_volume_single_sided", text="Single Sided")

View File

@ -33,8 +33,10 @@ class GPENCIL_MT_material_context_menu(Menu):
layout.operator(
"grease_pencil.material_copy_to_object",
text="Copy Material to Selected").only_active = True
layout.operator("grease_pencil.material_copy_to_object",
text="Copy All Materials to Selected").only_active = False
layout.operator(
"grease_pencil.material_copy_to_object",
text="Copy All Materials to Selected",
).only_active = False
else:
layout.operator("gpencil.material_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
@ -56,8 +58,10 @@ class GPENCIL_MT_material_context_menu(Menu):
layout.separator()
layout.operator("gpencil.materials_copy_to_object", text="Copy Material to Selected").only_active = True
layout.operator("gpencil.materials_copy_to_object",
text="Copy All Materials to Selected").only_active = False
layout.operator(
"gpencil.materials_copy_to_object",
text="Copy All Materials to Selected",
).only_active = False
layout.separator()

View File

@ -5,6 +5,23 @@
from bpy.types import Menu
class BrushAssetShelf:
bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'}
bl_default_preview_size = 48
@classmethod
def poll(cls, context):
prefs = context.preferences
if not prefs.experimental.use_extended_asset_browser:
return False
return context.mode == 'SCULPT'
@classmethod
def asset_poll(cls, asset):
return asset.id_type == 'BRUSH'
class UnifiedPaintPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
@ -1512,7 +1529,7 @@ def brush_basic_grease_pencil_paint_settings(layout, context, brush, *, compact=
row.prop(gp_settings, "caps_type", text="Caps Type")
elif grease_pencil_tool == 'ERASE':
layout.prop(gp_settings, "eraser_mode", expand=True)
if gp_settings.eraser_mode == "HARD":
if gp_settings.eraser_mode == 'HARD':
layout.prop(gp_settings, "use_keep_caps_eraser")
layout.prop(gp_settings, "use_active_layer_only")
elif grease_pencil_tool == 'TINT':

View File

@ -1092,8 +1092,10 @@ class PARTICLE_PT_physics_relations(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
row = layout.row()
row.template_list("UI_UL_list", "particle_targets", psys, "targets",
psys, "active_particle_target_index", rows=4)
row.template_list(
"UI_UL_list", "particle_targets", psys, "targets",
psys, "active_particle_target_index", rows=4,
)
col = row.column()
sub = col.row()
@ -1150,8 +1152,10 @@ class PARTICLE_PT_physics_fluid_interaction(ParticleButtonsPanel, Panel):
psys = context.particle_system
row = layout.row()
row.template_list("UI_UL_list", "particle_targets", psys, "targets",
psys, "active_particle_target_index", rows=4)
row.template_list(
"UI_UL_list", "particle_targets", psys, "targets",
psys, "active_particle_target_index", rows=4,
)
col = row.column()
sub = col.row()
@ -1324,8 +1328,10 @@ class PARTICLE_PT_boidbrain(ParticleButtonsPanel, Panel):
# layout.prop(state, "name", text="State name")
row = layout.row()
row.template_list("UI_UL_list", "particle_boids_rules", state,
"rules", state, "active_boid_rule_index", rows=4)
row.template_list(
"UI_UL_list", "particle_boids_rules", state,
"rules", state, "active_boid_rule_index", rows=4,
)
col = row.column()
sub = col.row()
@ -1619,8 +1625,10 @@ class PARTICLE_PT_render_collection_use_count(ParticleButtonsPanel, Panel):
layout.active = part.use_collection_count and not part.use_whole_collection
row = layout.row()
row.template_list("UI_UL_list", "particle_instance_weights", part, "instance_weights",
part, "active_instanceweight_index")
row.template_list(
"UI_UL_list", "particle_instance_weights", part, "instance_weights",
part, "active_instanceweight_index",
)
col = row.column()
sub = col.row()

View File

@ -38,8 +38,10 @@ class PHYSICS_PT_geometry_nodes(Panel):
calc_text = iface_("Calculate to Frame")
bake_text = iface_("Bake")
layout.operator("object.simulation_nodes_cache_calculate_to_frame",
text=calc_text, translate=False).selected = True
layout.operator(
"object.simulation_nodes_cache_calculate_to_frame",
text=calc_text, translate=False,
).selected = True
row = layout.row(align=True)
row.operator("object.simulation_nodes_cache_bake", text=bake_text, translate=False).selected = True

View File

@ -702,7 +702,14 @@ class RENDER_PT_eevee_next_clamping(RenderButtonsPanel, Panel):
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
pass
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
props = scene.eevee
col = layout.column()
col.prop(props, "clamp_world", text="World")
class RENDER_PT_eevee_next_clamping_surface(RenderButtonsPanel, Panel):
@ -775,6 +782,9 @@ class RENDER_PT_eevee_next_sampling_shadows(RenderButtonsPanel, Panel):
col.prop(props, "shadow_ray_count", text="Rays")
col.prop(props, "shadow_step_count", text="Steps")
col = layout.column()
col.prop(props, "shadow_resolution_scale", text="Resolution")
col = layout.column(align=False, heading="Volume Shadows")
row = col.row(align=True)
sub = row.row(align=True)

View File

@ -261,9 +261,11 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel):
col.prop(view_layer, "use_pass_cryptomatte_material", text="Material")
col.prop(view_layer, "use_pass_cryptomatte_asset", text="Asset")
col = layout.column()
col.active = any((view_layer.use_pass_cryptomatte_object,
view_layer.use_pass_cryptomatte_material,
view_layer.use_pass_cryptomatte_asset))
col.active = any((
view_layer.use_pass_cryptomatte_object,
view_layer.use_pass_cryptomatte_material,
view_layer.use_pass_cryptomatte_asset,
))
col.prop(view_layer, "pass_cryptomatte_depth", text="Levels")
if context.engine == 'BLENDER_EEVEE':
@ -298,8 +300,10 @@ class ViewLayerLightgroupsPanel(ViewLayerButtonsPanel, Panel):
row = layout.row()
col = row.column()
col.template_list("UI_UL_list", "lightgroups", view_layer,
"lightgroups", view_layer, "active_lightgroup_index", rows=3)
col.template_list(
"UI_UL_list", "lightgroups", view_layer,
"lightgroups", view_layer, "active_lightgroup_index", rows=3,
)
col = row.column()
sub = col.column(align=True)

View File

@ -21,14 +21,16 @@ class CLIP_UL_tracking_objects(UIList):
# assert(isinstance(item, bpy.types.MovieTrackingObject)
tobj = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.prop(tobj, "name", text="", emboss=False,
icon='CAMERA_DATA' if tobj.is_camera
else 'OBJECT_DATA')
layout.prop(
tobj, "name", text="", emboss=False,
icon='CAMERA_DATA' if tobj.is_camera else 'OBJECT_DATA',
)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="",
icon='CAMERA_DATA' if tobj.is_camera
else 'OBJECT_DATA')
layout.label(
text="",
icon='CAMERA_DATA' if tobj.is_camera else 'OBJECT_DATA',
)
class CLIP_PT_display(Panel):
@ -573,9 +575,13 @@ class CLIP_PT_tools_solve(CLIP_PT_tracking_panel, Panel):
col = layout.column(align=True)
col.scale_y = 2.0
col.operator("clip.solve_camera",
text="Solve Camera Motion" if tracking_object.is_camera
else "Solve Object Motion")
col.operator(
"clip.solve_camera",
text=(
"Solve Camera Motion" if tracking_object.is_camera else
"Solve Object Motion"
),
)
class CLIP_PT_tools_cleanup(CLIP_PT_tracking_panel, Panel):

View File

@ -795,8 +795,10 @@ class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
asset_data = asset_utils.SpaceAssetInfo.get_active_asset(context)
row = layout.row()
row.template_list("ASSETBROWSER_UL_metadata_tags", "asset_tags", asset_data, "tags",
asset_data, "active_tag", rows=4)
row.template_list(
"ASSETBROWSER_UL_metadata_tags", "asset_tags", asset_data, "tags",
asset_data, "active_tag", rows=4,
)
col = row.column(align=True)
col.operator("asset.tag_add", icon='ADD', text="")

View File

@ -264,10 +264,14 @@ class NLA_MT_strips(Menu):
layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
layout.operator("nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
layout.operator("nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
layout.operator(
"nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Full Stack)",
).use_upper_stack_evaluation = True
layout.operator(
"nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Lower Stack)",
).use_upper_stack_evaluation = False
class NLA_MT_strips_transform(Menu):
@ -325,10 +329,14 @@ class NLA_MT_context_menu(Menu):
layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
layout.operator("nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
layout.operator("nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
layout.operator(
"nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Full Stack)",
).use_upper_stack_evaluation = True
layout.operator(
"nla.tweakmode_enter",
text="Start Tweaking Strip Actions (Lower Stack)",
).use_upper_stack_evaluation = False
layout.separator()

View File

@ -991,298 +991,6 @@ class NODE_PT_node_tree_properties(Panel):
col.prop(group, "is_tool")
def draw_socket_item_in_list(uilist, layout, item, icon):
if uilist.layout_type in {'DEFAULT', 'COMPACT'}:
row = layout.row(align=True)
row.template_node_socket(color=item.color)
row.prop(item, "name", text="", emboss=False, icon_value=icon)
elif uilist.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.template_node_socket(color=item.color)
class NODE_UL_simulation_zone_items(UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
draw_socket_item_in_list(self, layout, item, icon)
class NODE_PT_simulation_zone_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Simulation State"
input_node_type = 'GeometryNodeSimulationInput'
output_node_type = 'GeometryNodeSimulationOutput'
@classmethod
def get_output_node(cls, context):
node = context.active_node
if node.bl_idname == cls.input_node_type:
return node.paired_output
if node.bl_idname == cls.output_node_type:
return node
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None or node.bl_idname not in [cls.input_node_type, cls.output_node_type]:
return False
if cls.get_output_node(context) is None:
return False
return True
def draw(self, context):
layout = self.layout
output_node = self.get_output_node(context)
split = layout.row()
split.template_list(
"NODE_UL_simulation_zone_items",
"",
output_node,
"state_items",
output_node,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.simulation_zone_item_add", icon='ADD', text="")
add_remove_col.operator("node.simulation_zone_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.simulation_zone_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.simulation_zone_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = output_node.active_item
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_item, "socket_type")
if active_item.socket_type in {'VECTOR', 'INT', 'BOOLEAN', 'FLOAT', 'RGBA', 'ROTATION'}:
layout.prop(active_item, "attribute_domain")
class NODE_UL_repeat_zone_items(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
draw_socket_item_in_list(self, layout, item, icon)
class NODE_PT_repeat_zone_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Repeat"
input_node_type = 'GeometryNodeRepeatInput'
output_node_type = 'GeometryNodeRepeatOutput'
@classmethod
def get_output_node(cls, context):
node = context.active_node
if node.bl_idname == cls.input_node_type:
return node.paired_output
if node.bl_idname == cls.output_node_type:
return node
return None
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None or node.bl_idname not in {cls.input_node_type, cls.output_node_type}:
return False
if cls.get_output_node(context) is None:
return False
return True
def draw(self, context):
layout = self.layout
output_node = self.get_output_node(context)
split = layout.row()
split.template_list(
"NODE_UL_repeat_zone_items",
"",
output_node,
"repeat_items",
output_node,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.repeat_zone_item_add", icon='ADD', text="")
add_remove_col.operator("node.repeat_zone_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = output_node.active_item
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_item, "socket_type")
layout.prop(output_node, "inspection_index")
class NODE_UL_bake_node_items(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
draw_socket_item_in_list(self, layout, item, icon)
class NODE_PT_bake_node_items(bpy.types.Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Bake Items"
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None:
return False
if node.bl_idname != "GeometryNodeBake":
return False
return True
def draw(self, context):
layout = self.layout
node = context.active_node
split = layout.row()
split.template_list(
"NODE_UL_bake_node_items",
"",
node,
"bake_items",
node,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.bake_node_item_add", icon='ADD', text="")
add_remove_col.operator("node.bake_node_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.bake_node_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.bake_node_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = node.active_item
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_item, "socket_type")
if active_item.socket_type in {'VECTOR', 'INT', 'BOOLEAN', 'FLOAT', 'RGBA', 'ROTATION'}:
layout.prop(active_item, "attribute_domain")
layout.prop(active_item, "is_attribute")
class NODE_PT_index_switch_node_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Index Switch"
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None or node.bl_idname != 'GeometryNodeIndexSwitch':
return False
return True
def draw(self, context):
layout = self.layout
node = context.active_node
layout.operator("node.index_switch_item_add", icon='ADD', text="Add Item")
col = layout.column()
for i, item in enumerate(node.index_switch_items):
row = col.row()
row.label(text=node.inputs[i + 1].name)
row.operator("node.index_switch_item_remove", icon='REMOVE', text="").index = i
class NODE_UL_enum_definition_items(bpy.types.UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
layout.prop(item, "name", text="", emboss=False, icon_value=icon)
class NODE_PT_menu_switch_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Menu Switch"
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None or node.bl_idname != "GeometryNodeMenuSwitch":
return False
return True
def draw(self, context):
node = context.active_node
layout = self.layout
split = layout.row()
split.template_list(
"NODE_UL_enum_definition_items",
"",
node.enum_definition,
"enum_items",
node.enum_definition,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.enum_definition_item_add", icon='ADD', text="")
add_remove_col.operator("node.enum_definition_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.enum_definition_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.enum_definition_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = node.enum_definition.active_item
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_item, "description")
# Grease Pencil properties
class NODE_PT_annotation(AnnotationDataPanel, Panel):
bl_space_type = 'NODE_EDITOR'
@ -1349,15 +1057,6 @@ classes = (
NODE_PT_quality,
NODE_PT_annotation,
NODE_PT_overlay,
NODE_UL_simulation_zone_items,
NODE_PT_simulation_zone_items,
NODE_UL_repeat_zone_items,
NODE_UL_bake_node_items,
NODE_PT_bake_node_items,
NODE_PT_index_switch_node_items,
NODE_PT_repeat_zone_items,
NODE_UL_enum_definition_items,
NODE_PT_menu_switch_items,
NODE_PT_active_node_properties,
node_panel(EEVEE_MATERIAL_PT_settings),

View File

@ -359,19 +359,25 @@ class OUTLINER_MT_liboverride(Menu):
def draw(self, _context):
layout = self.layout
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set",
text="Make").type = 'OVERRIDE_LIBRARY_CREATE_HIERARCHY'
layout.operator_menu_enum(
"outliner.liboverride_operation", "selection_set",
text="Make",
).type = 'OVERRIDE_LIBRARY_CREATE_HIERARCHY'
layout.operator_menu_enum(
"outliner.liboverride_operation",
"selection_set",
text="Reset").type = 'OVERRIDE_LIBRARY_RESET'
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set",
text="Clear").type = 'OVERRIDE_LIBRARY_CLEAR_SINGLE'
layout.operator_menu_enum(
"outliner.liboverride_operation", "selection_set",
text="Clear",
).type = 'OVERRIDE_LIBRARY_CLEAR_SINGLE'
layout.separator()
layout.operator_menu_enum("outliner.liboverride_troubleshoot_operation", "type",
text="Troubleshoot").selection_set = 'SELECTED'
layout.operator_menu_enum(
"outliner.liboverride_troubleshoot_operation", "type",
text="Troubleshoot",
).selection_set = 'SELECTED'
class OUTLINER_PT_filter(Panel):

View File

@ -320,6 +320,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel):
layout.prop(overlay_settings, "show_strip_retiming", text="Retiming")
layout.prop(overlay_settings, "show_thumbnails", text="Thumbnails")
layout.prop(overlay_settings, "show_grid", text="Grid")
layout.prop(st.cache_overlay, "show_cache", text="Cache")
layout.separator()
@ -329,25 +330,6 @@ class SEQUENCER_PT_sequencer_overlay(Panel):
layout.row().prop(overlay_settings, "waveform_display_style", expand=True)
class SEQUENCER_MT_view_cache(Menu):
bl_label = "Cache"
def draw(self, context):
layout = self.layout
ed = context.scene.sequence_editor
layout.prop(ed, "show_cache")
layout.separator()
col = layout.column()
col.enabled = ed.show_cache
col.prop(ed, "show_cache_final_out")
col.prop(ed, "show_cache_raw")
col.prop(ed, "show_cache_preprocessed")
col.prop(ed, "show_cache_composite")
class SEQUENCER_MT_range(Menu):
bl_label = "Range"
@ -481,10 +463,6 @@ class SEQUENCER_MT_view(Menu):
layout.menu("SEQUENCER_MT_range")
layout.separator()
if context.preferences.view.show_developer_ui:
layout.menu("SEQUENCER_MT_view_cache", text="Cache")
layout.separator()
layout.operator("render.opengl", text="Sequence Render Image", icon='RENDER_STILL').sequencer = True
props = layout.operator("render.opengl", text="Sequence Render Animation", icon='RENDER_ANIMATION')
props.animation = True
@ -2287,8 +2265,7 @@ class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel):
@classmethod
def poll(cls, context):
show_developer_ui = context.preferences.view.show_developer_ui
return cls.has_sequencer(context) and context.scene.sequence_editor and show_developer_ui
return cls.has_sequencer(context) and context.scene.sequence_editor
def draw(self, context):
layout = self.layout
@ -2305,6 +2282,38 @@ class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel):
col.prop(ed, "use_cache_final", text="Final")
class SEQUENCER_PT_cache_view_settings(SequencerButtonsPanel, Panel):
bl_label = "Display"
bl_category = "Cache"
bl_parent_id = "SEQUENCER_PT_cache_settings"
@classmethod
def poll(cls, context):
return cls.has_sequencer(context) and context.scene.sequence_editor
def draw_header(self, context):
cache_settings = context.space_data.cache_overlay
self.layout.prop(cache_settings, "show_cache", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
cache_settings = context.space_data.cache_overlay
layout.active = cache_settings.show_cache
col = layout.column(heading="Cache", align=True)
show_developer_ui = context.preferences.view.show_developer_ui
if show_developer_ui:
col.prop(cache_settings, "show_cache_raw", text="Raw")
col.prop(cache_settings, "show_cache_preprocessed", text="Preprocessed")
col.prop(cache_settings, "show_cache_composite", text="Composite")
col.prop(cache_settings, "show_cache_final_out", text="Final")
class SEQUENCER_PT_proxy_settings(SequencerButtonsPanel, Panel):
bl_label = "Proxy Settings"
bl_category = "Proxy"
@ -2688,7 +2697,7 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
split = box.split(factor=0.4)
split.label(text="{.2f}".format(sound_eq.curve_mapping.clip_min_x))
split.label(text="Hz")
split.alignment = "RIGHT"
split.alignment = 'RIGHT'
split.label(text="{.2f}".format(sound_eq.curve_mapping.clip_max_x))
box.template_curve_mapping(
sound_eq,
@ -2791,7 +2800,6 @@ classes = (
SEQUENCER_MT_editor_menus,
SEQUENCER_MT_range,
SEQUENCER_MT_view,
SEQUENCER_MT_view_cache,
SEQUENCER_MT_preview_zoom,
SEQUENCER_MT_proxy,
SEQUENCER_MT_select_handle,
@ -2854,6 +2862,7 @@ classes = (
SEQUENCER_PT_modifiers,
SEQUENCER_PT_cache_settings,
SEQUENCER_PT_cache_view_settings,
SEQUENCER_PT_strip_cache,
SEQUENCER_PT_proxy_settings,
SEQUENCER_PT_strip_proxy,

View File

@ -367,12 +367,8 @@ class TEXT_MT_edit_to3d(Menu):
def draw(self, _context):
layout = self.layout
layout.operator("text.to_3d_object",
text="One Object",
).split_lines = False
layout.operator("text.to_3d_object",
text="One Object Per Line",
).split_lines = True
layout.operator("text.to_3d_object", text="One Object").split_lines = False
layout.operator("text.to_3d_object", text="One Object Per Line").split_lines = True
class TEXT_MT_edit(Menu):

View File

@ -1579,6 +1579,23 @@ class _defs_sculpt:
draw_settings=draw_settings,
)
@ToolDef.from_fn
def trim_line():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("sculpt.trim_line_gesture")
layout.prop(props, "trim_solver", expand=False)
layout.prop(props, "trim_orientation", expand=False)
layout.prop(props, "use_cursor_depth", expand=False)
layout.prop(props, "use_limit_to_segment", expand=False)
return dict(
idname="builtin.line_trim",
label="Line Trim",
icon="ops.sculpt.line_trim",
widget=None,
keymap=(),
draw_settings=draw_settings,
)
@ToolDef.from_fn
def project_line():
def draw_settings(_context, layout, tool):
@ -3410,6 +3427,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
(
_defs_sculpt.trim_box,
_defs_sculpt.trim_lasso,
_defs_sculpt.trim_line,
),
_defs_sculpt.project_line,
None,

View File

@ -134,8 +134,10 @@ class TOPBAR_PT_gpencil_layers(Panel):
col = row.column()
layer_rows = 10
col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
rows=layer_rows, sort_reverse=True, sort_lock=True)
col.template_list(
"GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
rows=layer_rows, sort_reverse=True, sort_lock=True,
)
gpl = context.active_gpencil_layer
if gpl:
@ -377,10 +379,11 @@ class TOPBAR_MT_file_defaults(Menu):
display_name = bpy.path.display_name(iface_(app_template))
props = layout.operator("wm.read_factory_settings", text="Load Factory Blender Settings")
props.app_template = app_template
props = layout.operator("wm.read_factory_settings",
text=iface_("Load Factory {:s} Settings",
i18n_contexts.operator_default).format(display_name),
translate=False)
props = layout.operator(
"wm.read_factory_settings",
text=iface_("Load Factory {:s} Settings", i18n_contexts.operator_default).format(display_name),
translate=False,
)
props.app_template = app_template
props.use_factory_startup_app_template_only = True
del display_name

View File

@ -122,9 +122,11 @@ class USERPREF_MT_save_load(Menu):
if app_template:
display_name = bpy.path.display_name(iface_(app_template))
layout.operator("wm.read_factory_userpref", text="Load Factory Blender Preferences")
props = layout.operator("wm.read_factory_userpref",
text=iface_("Load Factory {:s} Preferences").format(display_name),
translate=False)
props = layout.operator(
"wm.read_factory_userpref",
text=iface_("Load Factory {:s} Preferences").format(display_name),
translate=False,
)
props.use_factory_startup_app_template_only = True
del display_name
else:
@ -673,16 +675,31 @@ class USERPREF_PT_system_os_settings(SystemPanel, CenterAlignMixIn, Panel):
return False
return True
def draw_centered(self, context, layout):
@staticmethod
def _draw_associate_supported_or_label(context, layout):
from sys import platform
associate_supported = True
if platform[:3] == "win":
if context.preferences.system.is_microsoft_store_install:
layout.label(text="Microsoft Store installation")
layout.label(text="Use Windows 'Default Apps' to associate with blend files")
associate_supported = False
return False
else:
# Linux.
if bpy.utils.resource_path('SYSTEM'):
layout.label(text="System Installation")
layout.label(text="File association is handled by the package manager")
return False
if associate_supported:
import os
if os.environ.get("SNAP"):
layout.label(text="Snap Package Installation")
layout.label(text="File association is handled by the package manager")
return False
return True
def draw_centered(self, context, layout):
if self._draw_associate_supported_or_label(context, layout):
layout.label(text="Open blend files with this Blender version")
split = layout.split(factor=0.5)
split.alignment = 'LEFT'

View File

@ -13,6 +13,7 @@ from bl_ui.properties_paint_common import (
brush_basic_texpaint_settings,
brush_basic_gpencil_weight_settings,
brush_basic_grease_pencil_weight_settings,
BrushAssetShelf,
)
from bl_ui.properties_grease_pencil_common import (
AnnotationDataPanel,
@ -787,7 +788,13 @@ class VIEW3D_HT_header(Header):
)
# Proportional editing
if object_mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL', 'EDIT_GPENCIL', 'OBJECT'}:
if object_mode in {
'EDIT',
'PARTICLE_EDIT',
'SCULPT_GPENCIL',
'EDIT_GPENCIL',
'OBJECT',
} and context.mode != 'EDIT_ARMATURE':
row = layout.row(align=True)
kw = {}
if object_mode == 'OBJECT':
@ -897,6 +904,8 @@ class VIEW3D_HT_header(Header):
if object_mode == 'PAINT_GPENCIL':
row = layout.row(align=True)
sub.prop(tool_settings, "use_gpencil_draw_onback", text="", icon='MOD_OPACITY')
sub.separator(factor=0.4)
row.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
@ -1186,31 +1195,35 @@ class VIEW3D_HT_header(Header):
@staticmethod
def _sculpt_automasking_icon(sculpt):
automask_enabled = (sculpt.use_automasking_topology or
sculpt.use_automasking_face_sets or
sculpt.use_automasking_boundary_edges or
sculpt.use_automasking_boundary_face_sets or
sculpt.use_automasking_cavity or
sculpt.use_automasking_cavity_inverted or
sculpt.use_automasking_start_normal or
sculpt.use_automasking_view_normal)
automask_enabled = (
sculpt.use_automasking_topology or
sculpt.use_automasking_face_sets or
sculpt.use_automasking_boundary_edges or
sculpt.use_automasking_boundary_face_sets or
sculpt.use_automasking_cavity or
sculpt.use_automasking_cavity_inverted or
sculpt.use_automasking_start_normal or
sculpt.use_automasking_view_normal
)
return "CLIPUV_DEHLT" if automask_enabled else "CLIPUV_HLT"
return 'CLIPUV_DEHLT' if automask_enabled else 'CLIPUV_HLT'
@staticmethod
def _gpencil_sculpt_automasking_icon(gpencil_sculpt):
automask_enabled = (gpencil_sculpt.use_automasking_stroke or
gpencil_sculpt.use_automasking_layer_stroke or
gpencil_sculpt.use_automasking_material_stroke or
gpencil_sculpt.use_automasking_material_active or
gpencil_sculpt.use_automasking_layer_active)
automask_enabled = (
gpencil_sculpt.use_automasking_stroke or
gpencil_sculpt.use_automasking_layer_stroke or
gpencil_sculpt.use_automasking_material_stroke or
gpencil_sculpt.use_automasking_material_active or
gpencil_sculpt.use_automasking_layer_active
)
return "CLIPUV_DEHLT" if automask_enabled else "CLIPUV_HLT"
return 'CLIPUV_DEHLT' if automask_enabled else 'CLIPUV_HLT'
@staticmethod
def _texture_mask_icon(ipaint):
mask_enabled = ipaint.use_stencil_layer or ipaint.use_cavity
return "CLIPUV_DEHLT" if mask_enabled else "CLIPUV_HLT"
return 'CLIPUV_DEHLT' if mask_enabled else 'CLIPUV_HLT'
class VIEW3D_MT_editor_menus(Menu):
@ -1262,7 +1275,7 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_mesh_add", text="Add", text_ctxt=i18n_contexts.operator_default)
elif mode_string == 'EDIT_CURVE':
layout.menu("VIEW3D_MT_curve_add", text="Add", text_ctxt=i18n_contexts.operator_default)
elif mode_string == "EDIT_CURVES":
elif mode_string == 'EDIT_CURVES':
layout.menu("VIEW3D_MT_edit_curves_add", text="Add", text_ctxt=i18n_contexts.operator_default)
elif mode_string == 'EDIT_SURFACE':
layout.menu("VIEW3D_MT_surface_add", text="Add", text_ctxt=i18n_contexts.operator_default)
@ -1300,8 +1313,8 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_edit_curves_segments")
layout.template_node_operator_asset_root_items()
elif mode_string == 'EDIT_GREASE_PENCIL':
layout.menu("VIEW3D_MT_edit_greasepencil_stroke")
layout.menu("VIEW3D_MT_edit_greasepencil_point")
layout.menu("VIEW3D_MT_edit_greasepencil_stroke")
elif obj:
if mode_string not in {'PAINT_TEXTURE', 'SCULPT_CURVES', 'SCULPT_GREASE_PENCIL'}:
@ -2552,8 +2565,10 @@ class VIEW3D_MT_surface_add(Menu):
layout.operator("surface.primitive_nurbs_surface_curve_add", text="Nurbs Curve", icon='SURFACE_NCURVE')
layout.operator("surface.primitive_nurbs_surface_circle_add", text="Nurbs Circle", icon='SURFACE_NCIRCLE')
layout.operator("surface.primitive_nurbs_surface_surface_add", text="Nurbs Surface", icon='SURFACE_NSURFACE')
layout.operator("surface.primitive_nurbs_surface_cylinder_add",
text="Nurbs Cylinder", icon='SURFACE_NCYLINDER')
layout.operator(
"surface.primitive_nurbs_surface_cylinder_add",
text="Nurbs Cylinder", icon='SURFACE_NCYLINDER',
)
layout.operator("surface.primitive_nurbs_surface_sphere_add", text="Nurbs Sphere", icon='SURFACE_NSPHERE')
layout.operator("surface.primitive_nurbs_surface_torus_add", text="Nurbs Torus", icon='SURFACE_NTORUS')
@ -2682,9 +2697,11 @@ class VIEW3D_MT_volume_add(Menu):
def draw(self, _context):
layout = self.layout
layout.operator("object.volume_import", text="Import OpenVDB...", icon='OUTLINER_DATA_VOLUME')
layout.operator("object.volume_add", text="Empty",
text_ctxt=i18n_contexts.id_volume,
icon='OUTLINER_DATA_VOLUME')
layout.operator(
"object.volume_add", text="Empty",
text_ctxt=i18n_contexts.id_volume,
icon='OUTLINER_DATA_VOLUME',
)
class VIEW3D_MT_grease_pencil_add(Menu):
@ -2757,9 +2774,11 @@ class VIEW3D_MT_add(Menu):
layout.separator()
layout.operator_menu_enum("object.empty_add", "type", text="Empty",
text_ctxt=i18n_contexts.id_id,
icon='OUTLINER_OB_EMPTY')
layout.operator_menu_enum(
"object.empty_add", "type", text="Empty",
text_ctxt=i18n_contexts.id_id,
icon='OUTLINER_OB_EMPTY',
)
layout.menu("VIEW3D_MT_image_add", text="Image", icon='OUTLINER_OB_IMAGE')
layout.separator()
@ -2878,7 +2897,8 @@ class VIEW3D_MT_object(Menu):
layout.menu("VIEW3D_MT_object_liboverride", icon='LIBRARY_DATA_OVERRIDE')
layout.menu("VIEW3D_MT_object_relations")
layout.menu("VIEW3D_MT_object_parent")
layout.menu("VIEW3D_MT_object_constraints")
layout.menu("VIEW3D_MT_object_modifiers", icon='MODIFIER')
layout.menu("VIEW3D_MT_object_constraints", icon='CONSTRAINT')
layout.menu("VIEW3D_MT_object_track")
layout.menu("VIEW3D_MT_make_links")
@ -3347,6 +3367,28 @@ class VIEW3D_MT_object_constraints(Menu):
layout.operator("object.constraints_clear")
class VIEW3D_MT_object_modifiers(Menu):
bl_label = "Modifiers"
def draw(self, _context):
active_object = bpy.context.active_object
supported_types = {'MESH', 'CURVE', 'CURVES', 'SURFACE', 'FONT', 'VOLUME', 'GREASEPENCIL'}
layout = self.layout
if active_object:
if active_object.type in supported_types:
layout.menu("OBJECT_MT_modifier_add", text="Add Modifier")
elif active_object.type == 'GPENCIL':
layout.operator("object.gpencil_modifier_add", text="Add Modifier")
layout.operator("object.modifiers_copy_to_selected", text="Copy Modifiers to Selected Objects")
layout.separator()
layout.operator("object.modifiers_clear")
class VIEW3D_MT_object_quick_effects(Menu):
bl_label = "Quick Effects"
@ -3594,6 +3636,19 @@ class VIEW3D_MT_gpencil_vertex_group(Menu):
layout.operator("gpencil.vertex_group_deselect", text="Deselect")
class VIEW3D_MT_greasepencil_vertex_group(Menu):
bl_label = "Vertex Groups"
def draw(self, context):
layout = self.layout
layout.operator_context = 'EXEC_AREA'
ob = context.active_object
layout.operator("object.vertex_group_add", text="Add New Group")
ob = context.active_object
class VIEW3D_MT_paint_weight_lock(Menu):
bl_label = "Vertex Group Locks"
@ -3752,6 +3807,9 @@ class VIEW3D_MT_sculpt(Menu):
props = layout.operator("sculpt.trim_lasso_gesture", text="Lasso Trim")
props.trim_mode = 'DIFFERENCE'
props = layout.operator("sculpt.trim_line_gesture", text="Line Trim")
props.trim_mode = 'DIFFERENCE'
props = layout.operator("sculpt.trim_box_gesture", text="Box Add")
props.trim_mode = 'JOIN'
@ -5976,6 +6034,10 @@ class VIEW3D_MT_edit_greasepencil(Menu):
layout.separator()
layout.operator("grease_pencil.extrude_move", text="Extrude")
layout.separator()
layout.operator("grease_pencil.copy", text="Copy", icon='COPYDOWN')
layout.operator("grease_pencil.paste", text="Paste", icon='PASTEDOWN')
@ -6008,6 +6070,7 @@ class VIEW3D_MT_edit_greasepencil_stroke(Menu):
layout.separator()
layout.operator("grease_pencil.cyclical_set", text="Close").type = 'CLOSE'
layout.operator("grease_pencil.cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
layout.operator_menu_enum("grease_pencil.caps_set", text="Set Caps", property="type")
layout.operator("grease_pencil.stroke_switch_direction")
@ -6025,6 +6088,10 @@ class VIEW3D_MT_edit_greasepencil_point(Menu):
layout = self.layout
layout.operator("grease_pencil.stroke_smooth", text="Smooth")
layout.separator()
layout.menu("VIEW3D_MT_greasepencil_vertex_group")
class VIEW3D_MT_edit_curves_add(Menu):
bl_label = "Add"
@ -6323,7 +6390,7 @@ class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu):
pie.operator("paint.visibility_invert", text="Invert Visible")
props = pie.operator("paint.hide_show_all", text="Show All")
props.action = "SHOW"
props.action = 'SHOW'
class VIEW3D_MT_wpaint_vgroup_lock_pie(Menu):
@ -8401,12 +8468,6 @@ class VIEW3D_MT_greasepencil_edit_context_menu(Menu):
col.separator()
# Copy/paste
col.operator("grease_pencil.copy", text="Copy", icon='COPYDOWN')
col.operator("grease_pencil.paste", text="Paste", icon='PASTEDOWN')
col.separator()
# Main Strokes Operators
col.operator("grease_pencil.stroke_subdivide", text="Subdivide")
col.operator("grease_pencil.stroke_subdivide_smooth", text="Subdivide and Smooth")
@ -8428,10 +8489,17 @@ class VIEW3D_MT_greasepencil_edit_context_menu(Menu):
col.separator()
# Copy/paste
col.operator("grease_pencil.copy", text="Copy", icon='COPYDOWN')
col.operator("grease_pencil.paste", text="Paste", icon='PASTEDOWN')
col.operator("grease_pencil.duplicate_move", text="Duplicate")
col.separator()
col.operator("grease_pencil.extrude_move", text="Extrude")
col.separator()
col.operator("grease_pencil.separate", text="Separate").mode = 'SELECTED'
# Removal Operators
@ -8445,12 +8513,6 @@ class VIEW3D_MT_greasepencil_edit_context_menu(Menu):
col.separator()
# Copy/paste
col.operator("grease_pencil.copy", text="Copy", icon='COPYDOWN')
col.operator("grease_pencil.paste", text="Paste", icon='PASTEDOWN')
col.separator()
# Main Strokes Operators
col.operator("grease_pencil.stroke_subdivide", text="Subdivide")
col.operator("grease_pencil.stroke_subdivide_smooth", text="Subdivide and Smooth")
@ -8475,10 +8537,17 @@ class VIEW3D_MT_greasepencil_edit_context_menu(Menu):
col.separator()
# Copy/paste
col.operator("grease_pencil.copy", text="Copy", icon='COPYDOWN')
col.operator("grease_pencil.paste", text="Paste", icon='PASTEDOWN')
col.operator("grease_pencil.duplicate_move", text="Duplicate")
col.separator()
col.operator("grease_pencil.extrude_move", text="Extrude")
col.separator()
col.operator("grease_pencil.separate", text="Separate").mode = 'SELECTED'
@ -9018,25 +9087,12 @@ class VIEW3D_PT_viewport_debug(Panel):
layout.prop(overlay, "use_debug_freeze_view_culling")
class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf):
class VIEW3D_AST_sculpt_brushes(BrushAssetShelf, bpy.types.AssetShelf):
# Experimental: Asset shelf for sculpt brushes, only shows up if both the
# "Asset Shelf" and the "Extended Asset Browser" experimental features are
# enabled.
bl_space_type = 'VIEW_3D'
@classmethod
def poll(cls, context):
prefs = context.preferences
if not prefs.experimental.use_extended_asset_browser:
return False
return context.mode == 'SCULPT'
@classmethod
def asset_poll(cls, asset):
return asset.id_type == 'BRUSH'
classes = (
VIEW3D_HT_header,
@ -9111,6 +9167,7 @@ classes = (
VIEW3D_MT_object_track,
VIEW3D_MT_object_collection,
VIEW3D_MT_object_constraints,
VIEW3D_MT_object_modifiers,
VIEW3D_MT_object_quick_effects,
VIEW3D_MT_object_showhide,
VIEW3D_MT_object_cleanup,
@ -9121,6 +9178,7 @@ classes = (
VIEW3D_MT_hook,
VIEW3D_MT_vertex_group,
VIEW3D_MT_gpencil_vertex_group,
VIEW3D_MT_greasepencil_vertex_group,
VIEW3D_MT_paint_weight,
VIEW3D_MT_paint_weight_lock,
VIEW3D_MT_sculpt,

View File

@ -1481,8 +1481,10 @@ class VIEW3D_PT_tools_particlemode_options(View3DPanel, Panel):
if pe.type == 'PARTICLES':
if ob.particle_systems:
if len(ob.particle_systems) > 1:
layout.template_list("UI_UL_list", "particle_systems", ob, "particle_systems",
ob.particle_systems, "active_index", rows=2, maxrows=3)
layout.template_list(
"UI_UL_list", "particle_systems", ob, "particle_systems",
ob.particle_systems, "active_index", rows=2, maxrows=3,
)
ptcache = ob.particle_systems.active.point_cache
else:
@ -1491,8 +1493,10 @@ class VIEW3D_PT_tools_particlemode_options(View3DPanel, Panel):
ptcache = md.point_cache
if ptcache and len(ptcache.point_caches) > 1:
layout.template_list("UI_UL_list", "particles_point_caches", ptcache, "point_caches",
ptcache.point_caches, "active_index", rows=2, maxrows=3)
layout.template_list(
"UI_UL_list", "particles_point_caches", ptcache, "point_caches",
ptcache.point_caches, "active_index", rows=2, maxrows=3,
)
if not pe.is_editable:
layout.label(text="Point cache must be baked")

View File

@ -46,15 +46,26 @@ class VIEW3D_PT_animation_layers(Panel):
# is `None`, and thus its `animation` property does not exist.
col.template_ID(context.window_manager, 'selected_animation')
col = layout.column(align=True)
col = layout.column(align=False)
anim = adt and adt.animation
if anim:
col.prop(adt, 'animation_binding_handle', text="Binding")
binding = [o for o in anim.bindings if o.handle == adt.animation_binding_handle]
binding_sub = col.column(align=True)
# Binding selector.
row = binding_sub.row(align=True)
row.prop(adt, 'animation_binding', text="Binding")
row.operator('anim.binding_unassign_object', text="", icon='X')
binding = anim.bindings.get(adt.animation_binding, None)
if binding:
col.prop(binding[0], 'name', text="Anim Binding Name")
else:
col.label(text="AN Binding Name: -")
binding_sub.prop(binding, 'name_display', text="Name")
internal_sub = binding_sub.box().column(align=True)
internal_sub.active = False
internal_sub.prop(adt, 'animation_binding_handle', text="handle")
if binding:
internal_sub.prop(binding, 'name', text="Internal Name")
if adt:
col.prop(adt, 'animation_binding_name', text="ADT Binding Name")
else:

View File

@ -80,9 +80,11 @@ def node_group_items(context):
# Filter out hidden node-trees.
if group.name.startswith('.'):
continue
yield NodeItem(node_tree_group_type[group.bl_idname],
label=group.name,
settings={"node_tree": "bpy.data.node_groups[{!r}]".format(group.name)})
yield NodeItem(
node_tree_group_type[group.bl_idname],
label=group.name,
settings={"node_tree": "bpy.data.node_groups[{!r}]".format(group.name)},
)
# only show input/output nodes inside node groups

View File

@ -84,11 +84,15 @@ class Animation : public ::Animation {
const Binding *binding_for_handle(binding_handle_t handle) const;
/**
* Set the binding name.
* Set the binding name, ensure it is unique, and propagate the new name to
* all data-blocks that use it.
*
* This has to be done on the Animation level to ensure each binding has a
* unique name within the Animation.
*
* \note This does NOT ensure the first two characters match the ID type of
* this binding. This is the caller's responsibility.
*
* \see Animation::binding_name_define
* \see Animation::binding_name_propagate
*/
@ -97,7 +101,8 @@ class Animation : public ::Animation {
/**
* Set the binding name, and ensure it is unique.
*
* This function usually isn't necessary, call #binding_name_set instead.
* \note This does NOT ensure the first two characters match the ID type of
* this binding. This is the caller's responsibility.
*
* \see Animation::binding_name_set
* \see Animation::binding_name_propagate
@ -106,10 +111,10 @@ class Animation : public ::Animation {
/**
* Update the `AnimData::animation_binding_name` field of any ID that is animated by
* this.Binding.
* this Binding.
*
* Should be called after `binding_name_define(binding)`. This is implemented as a separate
* function due to the need to access bmain, which is available in the RNA on-property-update
* function due to the need to access `bmain`, which is available in the RNA on-property-update
* handler, but not in the RNA property setter.
*/
void binding_name_propagate(Main &bmain, const Binding &binding);
@ -119,13 +124,29 @@ class Animation : public ::Animation {
Binding *binding_for_id(const ID &animated_id);
const Binding *binding_for_id(const ID &animated_id) const;
/**
* Create a new, unused Binding.
*
* The returned binding will be suitable for any ID type. After binding to an
* ID, it be limited to that ID's type.
*/
Binding &binding_add();
/**
* Create a new binding, named after the given ID, and limited to the ID's type.
*
* Note that this assigns neither this Animation nor the new Binding to the ID. This function
* merely initializes the Binding itself to suitable values to start animating this ID.
*/
Binding &binding_add_for_id(const ID &animated_id);
/** Assign this animation to the ID.
*
* \param binding: The binding this ID should be animated by, may be nullptr if it is to be
* assigned later. In that case, the ID will not actually receive any animation.
* \param animated_id: The ID that should be animated by this Animation data-block.
*
* \return whether the assignment was successful.
*/
bool assign_id(Binding *binding, ID &animated_id);
@ -166,6 +187,26 @@ class Animation : public ::Animation {
private:
Binding &binding_allocate();
/**
* Ensure the binding name prefix matches its ID type.
*
* This ensures that the first two characters match the ID type of
* this binding.
*
* \see Animation::binding_name_propagate
*/
void binding_name_ensure_prefix(Binding &binding);
/**
* Set the binding's ID type to that of the animated ID, ensure the name
* prefix is set accordingly, and that the name is unique within the
* Animation.
*
* \note This assumes that the binding has no ID type set yet. If it does, it
* is considered a bug to call this function.
*/
void binding_setup_for_id(Binding &binding, const ID &animated_id);
};
static_assert(sizeof(Animation) == sizeof(::Animation),
"DNA struct and its C++ wrapper must have the same size");
@ -335,25 +376,41 @@ class Binding : public ::AnimationBinding {
constexpr static binding_handle_t unassigned = 0;
/**
* Let the given ID receive animation from this binding.
*
* This is a low-level function; for most purposes you want
* #Animation::assign_id instead.
*
* \note This does _not_ set animated_id->adt->animation to the owner of this
* Binding. It's the caller's responsibility to do that.
*
* \return Whether this was possible. If the Binding was already bound to a
* specific ID type, and `animated_id` is of a different type, it will be
* refused. If the ID type cannot be animated at all, false is also returned.
*
* \see assign_animation
* \see Animation::assign_id
* Binding names consist of a two-character ID code, then the display name.
* This means that the minimum length of a valid name is 3 characters.
*/
bool connect_id(ID &animated_id);
constexpr static int name_length_min = 3;
/**
* Return the name prefix for the Binding's type.
*
* This is the ID name prefix, so "OB" for objects, "CA" for cameras, etc.
*/
std::string name_prefix_for_idtype() const;
/**
* Return the name without the prefix.
*
* \see name_prefix_for_idtype
*/
StringRefNull name_without_prefix() const;
/** Return whether this Binding is usable by this ID type. */
bool is_suitable_for(const ID &animated_id) const;
/** Return whether this Binding has an `idtype` set. */
bool has_idtype() const;
protected:
friend Animation;
/**
* Ensure the first two characters of the name match the ID type.
*
* \note This does NOT ensure name uniqueness within the Animation. That is
* the responsibility of the caller.
*/
void name_ensure_prefix();
};
static_assert(sizeof(Binding) == sizeof(::AnimationBinding),
"DNA struct and its C++ wrapper must have the same size");
@ -459,6 +516,19 @@ bool assign_animation(Animation &anim, ID &animated_id);
*/
void unassign_animation(ID &animated_id);
/**
* Clear the animation binding of this ID.
*
* `adt.binding_handle_name` is updated to reflect the current name of the
* binding, before un-assigning. This is to ensure that the stored name reflects
* the actual binding that was used, making re-binding trivial.
*
* \param adt the AnimData of the animated ID.
*
* \note this does not clear the Animation pointer, just the binding handle.
*/
void unassign_binding(AnimData &adt);
/**
* Return the Animation of this ID, or nullptr if it has none.
*/

View File

@ -89,4 +89,31 @@ int insert_vert_fcurve(FCurve *fcu,
const KeyframeSettings &settings,
eInsertKeyFlags flag);
/**
* \param sample_rate: indicates how many samples per frame should be generated.
* \param r_samples: Is expected to be an array large enough to hold `sample_count`.
*/
void sample_fcurve_segment(
FCurve *fcu, float start_frame, float sample_rate, float *samples, int sample_count);
enum class BakeCurveRemove {
NONE = 0,
IN_RANGE = 1,
OUT_RANGE = 2,
ALL = 3,
};
/**
* Creates keyframes in the given range at the given step interval.
* \param range: start and end frame to bake. Is inclusive on both ends.
* \param remove_existing: choice which keys to remove in relation to the given range.
*/
void bake_fcurve(FCurve *fcu, blender::int2 range, float step, BakeCurveRemove remove_existing);
/**
* Fill the space between selected keyframes with keyframes on full frames.
* E.g. With a key selected on frame 1 and 3 it will insert a key on frame 2.
*/
void bake_fcurve_segments(FCurve *fcu);
} // namespace blender::animrig

View File

@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup animrig
*
* \brief Functionality to interact with keying sets.
*/
namespace blender::animrig {
/** Mode for modify_keyframes. */
enum class ModifyKeyMode {
INSERT = 0,
DELETE,
};
/** Return codes for errors (with Relative KeyingSets). */
enum class ModifyKeyReturn {
SUCCESS = 0,
/** Context info was invalid for using the Keying Set. */
INVALID_CONTEXT = -1,
/** There isn't any type-info for generating paths from context. */
MISSING_TYPEINFO = -2,
};
} // namespace blender::animrig

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