BKE: Utilities to create curves and point clouds without attributes #120368
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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)]
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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'},
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'}
|
||||
|
||||
|
|
|
@ -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'}
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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'}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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="")
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue