MacOS: Enable support for EDR rendering #105662

Merged
Brecht Van Lommel merged 26 commits from Jason-Fielder/blender:macos_EDR_support into main 2023-08-09 14:25:23 +02:00
85 changed files with 5007 additions and 4519 deletions
Showing only changes of commit a540a1839f - Show all commits

View File

@ -873,6 +873,7 @@ static ShaderNode *add_node(Scene *scene,
voronoi->set_dimensions(b_voronoi_node.voronoi_dimensions());
voronoi->set_feature((NodeVoronoiFeature)b_voronoi_node.feature());
voronoi->set_metric((NodeVoronoiDistanceMetric)b_voronoi_node.distance());
voronoi->set_normalize(b_voronoi_node.normalize());
BL::TexMapping b_texture_mapping(b_voronoi_node.texture_mapping());
get_tex_mapping(voronoi, b_texture_mapping);
node = voronoi;

View File

@ -307,7 +307,12 @@ ccl_device_inline void bsdf_roughness_eta(const KernelGlobals kg,
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
*eta = CLOSURE_IS_REFRACTIVE(bsdf->type) ? 1.0f / bsdf->ior : bsdf->ior;
if (CLOSURE_IS_REFRACTION(bsdf->type) || CLOSURE_IS_GLASS(bsdf->type)) {
*eta = 1.0f / bsdf->ior;
}
else {
*eta = bsdf->ior;
}
break;
}
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: {
@ -594,7 +599,9 @@ ccl_device void bsdf_blur(KernelGlobals kg, ccl_private ShaderClosure *sc, float
}
ccl_device_inline Spectrum bsdf_albedo(ccl_private const ShaderData *sd,
ccl_private const ShaderClosure *sc)
ccl_private const ShaderClosure *sc,
const bool reflection,
const bool transmission)
{
Spectrum albedo = sc->weight;
/* Some closures include additional components such as Fresnel terms that cause their albedo to
@ -608,12 +615,16 @@ ccl_device_inline Spectrum bsdf_albedo(ccl_private const ShaderData *sd,
* extra overhead though. */
#if defined(__SVM__) || defined(__OSL__)
if (CLOSURE_IS_BSDF_MICROFACET(sc->type)) {
albedo *= bsdf_microfacet_estimate_fresnel(sd, (ccl_private const MicrofacetBsdf *)sc);
albedo *= bsdf_microfacet_estimate_fresnel(
sd, (ccl_private const MicrofacetBsdf *)sc, reflection, transmission);
}
else if (sc->type == CLOSURE_BSDF_PRINCIPLED_SHEEN_ID) {
kernel_assert(reflection);
albedo *= ((ccl_private const PrincipledSheenBsdf *)sc)->avg_value;
}
else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) {
/* TODO(lukas): Principled Hair could also be split into a glossy and a transmission component,
* similar to Glass BSDFs. */
albedo *= bsdf_principled_hair_albedo(sd, sc);
}
#endif

View File

@ -338,17 +338,20 @@ ccl_device_inline void microfacet_ggx_preserve_energy(KernelGlobals kg,
* For better results, we'd be blending between this and Fss based on roughness, but that
* would involve storing or recomputing Fss, which is probably not worth it. */
ccl_device Spectrum bsdf_microfacet_estimate_fresnel(ccl_private const ShaderData *sd,
ccl_private const MicrofacetBsdf *bsdf)
ccl_private const MicrofacetBsdf *bsdf,
const bool reflection,
const bool transmission)
{
const bool is_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool is_refractive = CLOSURE_IS_REFRACTIVE(bsdf->type);
const bool m_refraction = CLOSURE_IS_REFRACTION(bsdf->type);
const bool m_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool m_reflection = !(m_refraction || m_glass);
Spectrum albedo = zero_spectrum();
if (!is_refractive || is_glass) {
if (reflection && (m_reflection || m_glass)) {
/* BSDF has a reflective lobe. */
albedo += microfacet_fresnel(bsdf, sd->wi, bsdf->N, false);
}
if (is_refractive) {
if (transmission && (m_refraction || m_glass)) {
/* BSDF has a refractive lobe (unless there's TIR). */
albedo += microfacet_fresnel(bsdf, sd->wi, bsdf->N, true);
}
@ -455,8 +458,12 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
}
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
const bool m_refractive = CLOSURE_IS_REFRACTIVE(bsdf->type);
/* Refraction: Only consider BTDF
* Glass: Consider both BRDF and BTDF, mix based on Fresnel
* Reflection: Only consider BRDF */
const bool m_refraction = CLOSURE_IS_REFRACTION(bsdf->type);
const bool m_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool m_reflection = !(m_refraction || m_glass);
const float3 N = bsdf->N;
const float cos_NI = dot(N, wi);
@ -466,7 +473,7 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
const float alpha_x = bsdf->alpha_x;
const float alpha_y = bsdf->alpha_y;
const bool is_refraction = (cos_NO < 0.0f);
const bool is_transmission = (cos_NO < 0.0f);
/* Check whether the pair of directions is valid for evaluation:
* - Incoming direction has to be in the upper hemisphere (Cycles convention)
@ -475,15 +482,15 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
* - Purely reflective closures can't have refraction.
* - Purely refractive closures can't have reflection.
*/
if ((cos_NI <= 0) || (alpha_x * alpha_y <= 5e-7f) || ((cos_NgO < 0.0f) != is_refraction) ||
(is_refraction && !m_refractive) || (!is_refraction && m_refractive && !m_glass))
if ((cos_NI <= 0) || (alpha_x * alpha_y <= 5e-7f) || ((cos_NgO < 0.0f) != is_transmission) ||
(is_transmission && m_reflection) || (!is_transmission && m_refraction))
{
*pdf = 0.0f;
return zero_spectrum();
}
/* Compute half vector. */
float3 H = is_refraction ? -(bsdf->ior * wo + wi) : (wi + wo);
float3 H = is_transmission ? -(bsdf->ior * wo + wi) : (wi + wo);
const float inv_len_H = 1.0f / len(H);
H *= inv_len_H;
@ -491,7 +498,7 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
float D, lambdaI, lambdaO;
/* TODO: add support for anisotropic transmission. */
if (alpha_x == alpha_y || is_refraction) { /* Isotropic. */
if (alpha_x == alpha_y || is_transmission) { /* Isotropic. */
float alpha2 = alpha_x * alpha_y;
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) {
@ -523,19 +530,19 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
}
float common = D / cos_NI *
(is_refraction ? sqr(bsdf->ior * inv_len_H) * fabsf(dot(H, wi) * dot(H, wo)) :
0.25f);
(is_transmission ? sqr(bsdf->ior * inv_len_H) * fabsf(dot(H, wi) * dot(H, wo)) :
0.25f);
float lobe_pdf = 1.0f;
if (m_glass) {
float fresnel = fresnel_dielectric_cos(dot(H, wi), bsdf->ior);
float reflect_pdf = (fresnel == 1.0f) ? 1.0f : clamp(fresnel, 0.125f, 0.875f);
lobe_pdf = is_refraction ? (1.0f - reflect_pdf) : reflect_pdf;
lobe_pdf = is_transmission ? (1.0f - reflect_pdf) : reflect_pdf;
}
*pdf = common * lobe_pdf / (1.0f + lambdaI);
const Spectrum F = microfacet_fresnel(bsdf, wi, H, is_refraction);
const Spectrum F = microfacet_fresnel(bsdf, wi, H, is_transmission);
return F * common / (1.0f + lambdaO + lambdaI);
}
@ -554,7 +561,9 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
const float m_eta = bsdf->ior;
const bool m_refractive = CLOSURE_IS_REFRACTIVE(bsdf->type);
const bool m_refraction = CLOSURE_IS_REFRACTION(bsdf->type);
const bool m_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool m_reflection = !(m_refraction || m_glass);
const float alpha_x = bsdf->alpha_x;
const float alpha_y = bsdf->alpha_y;
bool m_singular = (m_type == MicrofacetType::SHARP) || (alpha_x * alpha_y <= 5e-7f);
@ -564,7 +573,7 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
if (cos_NI <= 0) {
*eval = zero_spectrum();
*pdf = 0.0f;
return (m_refractive ? LABEL_TRANSMIT : LABEL_REFLECT) |
return (m_reflection ? LABEL_REFLECT : LABEL_TRANSMIT) |
(m_singular ? LABEL_SINGULAR : LABEL_GLOSSY);
}
@ -603,13 +612,13 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
bool valid;
bool do_refract;
float lobe_pdf;
if (m_refractive) {
if (m_refraction || m_glass) {
bool inside;
float fresnel = fresnel_dielectric(m_eta, H, wi, wo, &inside);
valid = !inside;
/* For glass closures, we decide between reflection and refraction here. */
if (CLOSURE_IS_GLASS(bsdf->type)) {
if (m_glass) {
if (fresnel == 1.0f) {
/* TIR, reflection is the only option. */
do_refract = false;
@ -725,7 +734,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_conductor(KernelGlobals kg,
{
bsdf->fresnel_type = MicrofacetFresnel::CONDUCTOR;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
if (preserve_energy) {
/* In order to estimate Fss of the conductor, we fit the F82-tint model to it based on the
@ -748,7 +757,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_dielectric_tint(
{
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC_TINT;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
if (preserve_energy) {
/* Assume that the transmissive tint makes up most of the overall color. */
@ -765,7 +774,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_generalized_schlick(
{
bsdf->fresnel_type = MicrofacetFresnel::GENERALIZED_SCHLICK;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
if (preserve_energy) {
Spectrum Fss = one_spectrum();
@ -852,7 +861,7 @@ ccl_device int bsdf_microfacet_ggx_clearcoat_setup(ccl_private MicrofacetBsdf *b
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC;
bsdf->energy_scale = 1.0f;
bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
return SD_BSDF | SD_BSDF_HAS_EVAL;
}

View File

@ -67,7 +67,7 @@ ccl_device_forceinline void film_write_denoising_features_surface(KernelGlobals
}
}
Spectrum closure_albedo = bsdf_albedo(sd, sc);
Spectrum closure_albedo = bsdf_albedo(sd, sc, true, true);
if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) {
diffuse_albedo += closure_albedo;
sum_nonspecular_weight += sc->sample_weight;

View File

@ -20,36 +20,61 @@ CCL_NAMESPACE_BEGIN
* them separately. */
ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval,
const ClosureType closure_type,
ccl_private const ShaderClosure *sc,
const float3 wo,
Spectrum value)
{
eval->diffuse = zero_spectrum();
eval->glossy = zero_spectrum();
if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) {
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
eval->diffuse = value;
}
else if (CLOSURE_IS_BSDF_GLOSSY(closure_type)) {
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) {
eval->glossy = value;
}
else if (CLOSURE_IS_GLASS(sc->type)) {
/* Glass can count as glossy or transmission, depending on which side we end up on. */
if (dot(sc->N, wo) > 0.0f) {
eval->glossy = value;
}
}
eval->sum = value;
}
ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, Spectrum value)
{
eval->diffuse = zero_spectrum();
eval->glossy = zero_spectrum();
eval->sum = value;
}
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval,
const ClosureType closure_type,
ccl_private const ShaderClosure *sc,
const float3 wo,
Spectrum value)
{
if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) {
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
eval->diffuse += value;
}
else if (CLOSURE_IS_BSDF_GLOSSY(closure_type)) {
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) {
eval->glossy += value;
}
else if (CLOSURE_IS_GLASS(sc->type)) {
if (dot(sc->N, wo) > 0.0f) {
eval->glossy += value;
}
}
eval->sum += value;
}
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval, Spectrum value)
{
eval->sum += value;
}
ccl_device_inline bool bsdf_eval_is_zero(ccl_private BsdfEval *eval)
{
return is_zero(eval->sum);

View File

@ -979,7 +979,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
bool found_refractive_microfacet_bsdf = false;
for (int ci = 0; ci < sd_mnee->num_closure; ci++) {
ccl_private ShaderClosure *bsdf = &sd_mnee->closure[ci];
if (CLOSURE_IS_REFRACTIVE(bsdf->type)) {
if (CLOSURE_IS_REFRACTION(bsdf->type) || CLOSURE_IS_GLASS(bsdf->type)) {
/* Note that Glass closures are treated as refractive further below. */
found_refractive_microfacet_bsdf = true;

View File

@ -140,11 +140,12 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
{
/* Filter out closures. */
if (kernel_data.integrator.filter_closures) {
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_EMISSION) {
const int filter_closures = kernel_data.integrator.filter_closures;
if (filter_closures & FILTER_CLOSURE_EMISSION) {
sd->closure_emission_background = zero_spectrum();
}
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) {
if (filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) {
sd->flag &= ~SD_BSDF_HAS_EVAL;
}
@ -152,19 +153,20 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if ((CLOSURE_IS_BSDF_DIFFUSE(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIFFUSE)) ||
(CLOSURE_IS_BSDF_GLOSSY(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_GLOSSY)) ||
(CLOSURE_IS_BSDF_TRANSMISSION(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSMISSION)))
const bool filter_diffuse = (filter_closures & FILTER_CLOSURE_DIFFUSE);
const bool filter_glossy = (filter_closures & FILTER_CLOSURE_GLOSSY);
const bool filter_transmission = (filter_closures & FILTER_CLOSURE_TRANSMISSION);
const bool filter_glass = filter_glossy && filter_transmission;
if ((CLOSURE_IS_BSDF_DIFFUSE(sc->type) && filter_diffuse) ||
(CLOSURE_IS_BSDF_GLOSSY(sc->type) && filter_glossy) ||
(CLOSURE_IS_BSDF_TRANSMISSION(sc->type) && filter_transmission) ||
(CLOSURE_IS_GLASS(sc->type) && filter_glass))
{
sc->type = CLOSURE_NONE_ID;
sc->sample_weight = 0.0f;
}
else if ((CLOSURE_IS_BSDF_TRANSPARENT(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSPARENT)))
{
(filter_closures & FILTER_CLOSURE_TRANSPARENT))) {
sc->type = CLOSURE_HOLDOUT_ID;
sc->sample_weight = 0.0f;
sd->flag |= SD_HOLDOUT;
@ -268,6 +270,13 @@ ccl_device_forceinline bool _surface_shader_exclude(ClosureType type, uint light
return true;
}
}
/* Glass closures are both glossy and transmissive, so only exclude them if both are filtered. */
const uint exclude_glass = SHADER_EXCLUDE_TRANSMIT | SHADER_EXCLUDE_GLOSSY;
if ((light_shader_flags & exclude_glass) == exclude_glass) {
if (CLOSURE_IS_GLASS(type)) {
return true;
}
}
return false;
}
@ -295,7 +304,7 @@ ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
Spectrum eval = bsdf_eval(kg, sd, sc, wo, &bsdf_pdf);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
bsdf_eval_accum(result_eval, sc, wo, eval * sc->weight);
sum_pdf += bsdf_pdf * sc->sample_weight;
}
}
@ -318,7 +327,7 @@ ccl_device_inline float surface_shader_bsdf_eval_pdfs(const KernelGlobals kg,
* factors drop out when using balance heuristic weighting. */
float sum_pdf = 0.0f;
float sum_sample_weight = 0.0f;
bsdf_eval_init(result_eval, CLOSURE_NONE_ID, zero_spectrum());
bsdf_eval_init(result_eval, zero_spectrum());
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
@ -328,7 +337,7 @@ ccl_device_inline float surface_shader_bsdf_eval_pdfs(const KernelGlobals kg,
Spectrum eval = bsdf_eval(kg, sd, sc, wo, &bsdf_pdf);
kernel_assert(bsdf_pdf >= 0.0f);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
bsdf_eval_accum(result_eval, sc, wo, eval * sc->weight);
sum_pdf += bsdf_pdf * sc->sample_weight;
kernel_assert(bsdf_pdf * sc->sample_weight >= 0.0f);
pdfs[i] = bsdf_pdf * sc->sample_weight;
@ -369,7 +378,7 @@ ccl_device_inline
ccl_private BsdfEval *bsdf_eval,
const uint light_shader_flags)
{
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum());
bsdf_eval_init(bsdf_eval, zero_spectrum());
float pdf = _surface_shader_bsdf_eval_mis(
kg, sd, wo, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
@ -499,7 +508,7 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_mis(KernelGlobals kg,
/* Initialize to zero. */
int label = LABEL_NONE;
Spectrum eval = zero_spectrum();
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, eval);
bsdf_eval_init(bsdf_eval, eval);
*unguided_bsdf_pdf = 0.0f;
float guide_pdf = 0.0f;
@ -570,7 +579,7 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_mis(KernelGlobals kg,
# endif
if (*unguided_bsdf_pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
bsdf_eval_init(bsdf_eval, sc, *wo, eval * sc->weight);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
@ -622,7 +631,7 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_ris(KernelGlobals kg,
/* Initialize to zero. */
int label = LABEL_NONE;
Spectrum eval = zero_spectrum();
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, eval);
bsdf_eval_init(bsdf_eval, eval);
*unguided_bsdf_pdf = 0.0f;
float guide_pdf = 0.0f;
@ -652,7 +661,8 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_ris(KernelGlobals kg,
&ris_samples[0].sampled_roughness,
&ris_samples[0].eta);
bsdf_eval_init(&ris_samples[0].bsdf_eval, sc->type, ris_samples[0].eval * sc->weight);
bsdf_eval_init(
&ris_samples[0].bsdf_eval, sc, ris_samples[0].wo, ris_samples[0].eval * sc->weight);
if (ris_samples[0].bsdf_pdf > 0.0f) {
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
@ -679,7 +689,7 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_ris(KernelGlobals kg,
// generate the second RIS candidate using a sample from the guiding distribution
// ------------------------------------------------------------------------------
float unguided_bsdf_pdfs[MAX_CLOSURE];
bsdf_eval_init(&ris_samples[1].bsdf_eval, CLOSURE_NONE_ID, eval);
bsdf_eval_init(&ris_samples[1].bsdf_eval, eval);
ris_samples[1].guide_pdf = guiding_bsdf_sample(
kg, state, float3_to_float2(ris_samples[1].rand), &ris_samples[1].wo);
ris_samples[1].guide_pdf *= (1.0f - bssrdf_sampling_prob);
@ -812,7 +822,7 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_ris(KernelGlobals kg,
# endif
if (*unguided_bsdf_pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
bsdf_eval_init(bsdf_eval, sc, *wo, eval * sc->weight);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
@ -914,7 +924,7 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
label = bsdf_sample(kg, sd, sc, path_flag, rand_bsdf, &eval, wo, pdf, sampled_roughness, eta);
if (*pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
bsdf_eval_init(bsdf_eval, sc, *wo, eval * sc->weight);
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
@ -922,7 +932,7 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
}
}
else {
bsdf_eval_init(bsdf_eval, sc->type, zero_spectrum());
bsdf_eval_init(bsdf_eval, zero_spectrum());
}
return label;
@ -994,7 +1004,7 @@ ccl_device Spectrum surface_shader_diffuse(KernelGlobals kg, ccl_private const S
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type))
eval += bsdf_albedo(sd, sc);
eval += bsdf_albedo(sd, sc, true, true);
}
return eval;
@ -1007,8 +1017,8 @@ ccl_device Spectrum surface_shader_glossy(KernelGlobals kg, ccl_private const Sh
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_GLOSSY(sc->type))
eval += bsdf_albedo(sd, sc);
if (CLOSURE_IS_BSDF_GLOSSY(sc->type) || CLOSURE_IS_GLASS(sc->type))
eval += bsdf_albedo(sd, sc, true, false);
}
return eval;
@ -1021,8 +1031,8 @@ ccl_device Spectrum surface_shader_transmission(KernelGlobals kg, ccl_private co
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type))
eval += bsdf_albedo(sd, sc);
if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type) || CLOSURE_IS_GLASS(sc->type))
eval += bsdf_albedo(sd, sc, false, true);
}
return eval;

View File

@ -217,7 +217,7 @@ ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderDa
Spectrum eval = volume_phase_eval(sd, svc, wo, &phase_pdf);
if (phase_pdf != 0.0f) {
bsdf_eval_accum(result_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_accum(result_eval, eval);
sum_pdf += phase_pdf * svc->sample_weight;
}
@ -237,7 +237,7 @@ ccl_device float volume_shader_phase_eval(KernelGlobals kg,
Spectrum eval = volume_phase_eval(sd, svc, wo, &phase_pdf);
if (phase_pdf != 0.0f) {
bsdf_eval_accum(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_accum(phase_eval, eval);
}
return phase_pdf;
@ -250,7 +250,7 @@ ccl_device float volume_shader_phase_eval(KernelGlobals kg,
const float3 wo,
ccl_private BsdfEval *phase_eval)
{
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
bsdf_eval_init(phase_eval, zero_spectrum());
float pdf = _volume_shader_phase_eval_mis(sd, phases, wo, -1, phase_eval, 0.0f, 0.0f);
@ -300,7 +300,7 @@ ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
float guide_pdf = 0.0f;
*sampled_roughness = 1.0f - fabsf(svc->g);
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
bsdf_eval_init(phase_eval, zero_spectrum());
if (sample_guiding) {
/* Sample guiding distribution. */
@ -320,7 +320,7 @@ ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
label = volume_phase_sample(sd, svc, rand_phase, &eval, wo, unguided_phase_pdf);
if (*unguided_phase_pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_init(phase_eval, eval);
*phase_pdf = *unguided_phase_pdf;
if (use_volume_guiding) {
@ -332,7 +332,7 @@ ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
}
else {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
bsdf_eval_init(phase_eval, zero_spectrum());
}
kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
@ -359,7 +359,7 @@ ccl_device int volume_shader_phase_sample(KernelGlobals kg,
int label = volume_phase_sample(sd, svc, rand_phase, &eval, wo, pdf);
if (*pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_init(phase_eval, eval);
}
return label;

View File

@ -316,7 +316,7 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
return;
}
point_to_centroid = -bcone.axis;
cos_theta_u = fast_cosf(bcone.theta_o);
cos_theta_u = fast_cosf(bcone.theta_o + bcone.theta_e);
distance = 1.0f;
}
else {

View File

@ -113,11 +113,13 @@ file(GLOB SRC_OSL_HEADER_DIST ${OSL_SHADER_DIR}/*.h)
set(SRC_OSL_HEADERS
node_color.h
node_color_blend.h
node_fractal_voronoi.h
node_fresnel.h
node_hash.h
node_math.h
node_noise.h
node_ramp_util.h
node_voronoi.h
stdcycles.h
${SRC_OSL_HEADER_DIST}
)

View File

@ -0,0 +1,137 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#include "node_voronoi.h"
#include "stdcycles.h"
#include "vector2.h"
#include "vector4.h"
#define vector3 point
#define FRACTAL_VORONOI_X_FX(T) \
VoronoiOutput fractal_voronoi_x_fx(VoronoiParams params, T coord) \
{ \
float amplitude = 1.0; \
float max_amplitude = 0.0; \
float scale = 1.0; \
\
VoronoiOutput Output; \
Output.Distance = 0.0; \
Output.Color = color(0.0, 0.0, 0.0); \
Output.Position = vector4(0.0, 0.0, 0.0, 0.0); \
int zero_input = params.detail == 0.0 || params.roughness == 0.0 || params.lacunarity == 0.0; \
\
for (int i = 0; i <= ceil(params.detail); ++i) { \
VoronoiOutput octave; \
if (params.feature == "f1") { \
octave = voronoi_f1(params, coord * scale); \
} \
else if (params.feature == "smooth_f1") { \
octave = voronoi_smooth_f1(params, coord * scale); \
} \
else { \
octave = voronoi_f2(params, coord * scale); \
} \
\
if (zero_input) { \
max_amplitude = 1.0; \
Output = octave; \
break; \
} \
else if (i <= params.detail) { \
max_amplitude += amplitude; \
Output.Distance += octave.Distance * amplitude; \
Output.Color += octave.Color * amplitude; \
Output.Position = mix(Output.Position, octave.Position / scale, amplitude); \
scale *= params.lacunarity; \
amplitude *= params.roughness; \
} \
else { \
float remainder = params.detail - floor(params.detail); \
if (remainder != 0.0) { \
max_amplitude = mix(max_amplitude, max_amplitude + amplitude, remainder); \
Output.Distance = mix( \
Output.Distance, Output.Distance + octave.Distance * amplitude, remainder); \
Output.Color = mix(Output.Color, Output.Color + octave.Color * amplitude, remainder); \
Output.Position = mix(Output.Position, \
mix(Output.Position, octave.Position / scale, amplitude), \
remainder); \
} \
} \
} \
\
if (params.normalize) { \
Output.Distance /= max_amplitude * params.max_distance; \
Output.Color /= max_amplitude; \
} \
\
Output.Position = safe_divide(Output.Position, params.scale); \
\
return Output; \
}
#define FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(T) \
float fractal_voronoi_distance_to_edge(VoronoiParams params, T coord) \
{ \
float amplitude = 1.0; \
float max_amplitude = 0.5 + 0.5 * params.randomness; \
float scale = 1.0; \
float distance = 8.0; \
\
int zero_input = params.detail == 0.0 || params.roughness == 0.0 || params.lacunarity == 0.0; \
\
for (int i = 0; i <= ceil(params.detail); ++i) { \
float octave_distance = voronoi_distance_to_edge(params, coord * scale); \
\
if (zero_input) { \
distance = octave_distance; \
break; \
} \
else if (i <= params.detail) { \
max_amplitude = mix(max_amplitude, (0.5 + 0.5 * params.randomness) / scale, amplitude); \
distance = mix(distance, min(distance, octave_distance / scale), amplitude); \
scale *= params.lacunarity; \
amplitude *= params.roughness; \
} \
else { \
float remainder = params.detail - floor(params.detail); \
if (remainder != 0.0) { \
float lerp_amplitude = mix( \
max_amplitude, (0.5 + 0.5 * params.randomness) / scale, amplitude); \
max_amplitude = mix(max_amplitude, lerp_amplitude, remainder); \
float lerp_distance = mix(distance, min(distance, octave_distance / scale), amplitude); \
distance = mix(distance, min(distance, lerp_distance), remainder); \
} \
} \
} \
\
if (params.normalize) { \
distance /= max_amplitude; \
} \
\
return distance; \
}
/* **** 1D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(float)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(float)
/* **** 2D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(vector2)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(vector2)
/* **** 3D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(vector3)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(vector3)
/* **** 4D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(vector4)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(vector4)

View File

@ -0,0 +1,920 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#include "node_hash.h"
#include "stdcycles.h"
#include "vector2.h"
#include "vector4.h"
#define vector3 point
struct VoronoiParams {
float scale;
float detail;
float roughness;
float lacunarity;
float smoothness;
float exponent;
float randomness;
float max_distance;
int normalize;
string feature;
string metric;
};
struct VoronoiOutput {
float Distance;
color Color;
vector4 Position;
};
/* **** Distance Functions **** */
float distance(float a, float b)
{
return abs(a - b);
}
float distance(vector2 a, vector2 b)
{
return length(a - b);
}
float distance(vector4 a, vector4 b)
{
return length(a - b);
}
float voronoi_distance(float a, float b)
{
return abs(a - b);
}
float voronoi_distance(vector2 a, vector2 b, VoronoiParams params)
{
if (params.metric == "euclidean") {
return distance(a, b);
}
else if (params.metric == "manhattan") {
return abs(a.x - b.x) + abs(a.y - b.y);
}
else if (params.metric == "chebychev") {
return max(abs(a.x - b.x), abs(a.y - b.y));
}
else if (params.metric == "minkowski") {
return pow(pow(abs(a.x - b.x), params.exponent) + pow(abs(a.y - b.y), params.exponent),
1.0 / params.exponent);
}
else {
return 0.0;
}
}
float voronoi_distance(vector3 a, vector3 b, VoronoiParams params)
{
if (params.metric == "euclidean") {
return distance(a, b);
}
else if (params.metric == "manhattan") {
return abs(a[0] - b[0]) + abs(a[1] - b[1]) + abs(a[2] - b[2]);
}
else if (params.metric == "chebychev") {
return max(abs(a[0] - b[0]), max(abs(a[1] - b[1]), abs(a[2] - b[2])));
}
else if (params.metric == "minkowski") {
return pow(pow(abs(a[0] - b[0]), params.exponent) + pow(abs(a[1] - b[1]), params.exponent) +
pow(abs(a[2] - b[2]), params.exponent),
1.0 / params.exponent);
}
else {
return 0.0;
}
}
float voronoi_distance(vector4 a, vector4 b, VoronoiParams params)
{
if (params.metric == "euclidean") {
return distance(a, b);
}
else if (params.metric == "manhattan") {
return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) + abs(a.w - b.w);
}
else if (params.metric == "chebychev") {
return max(abs(a.x - b.x), max(abs(a.y - b.y), max(abs(a.z - b.z), abs(a.w - b.w))));
}
else if (params.metric == "minkowski") {
return pow(pow(abs(a.x - b.x), params.exponent) + pow(abs(a.y - b.y), params.exponent) +
pow(abs(a.z - b.z), params.exponent) + pow(abs(a.w - b.w), params.exponent),
1.0 / params.exponent);
}
else {
return 0.0;
}
}
/* **** Safe Division **** */
vector2 safe_divide(vector2 a, float b)
{
return vector2((b != 0.0) ? a.x / b : 0.0, (b != 0.0) ? a.y / b : 0.0);
}
vector4 safe_divide(vector4 a, float b)
{
return vector4((b != 0.0) ? a.x / b : 0.0,
(b != 0.0) ? a.y / b : 0.0,
(b != 0.0) ? a.z / b : 0.0,
(b != 0.0) ? a.w / b : 0.0);
}
/*
* SPDX-License-Identifier: MIT
* Original code is copyright (c) 2013 Inigo Quilez.
*
* Smooth Voronoi:
* - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
*
* Distance To Edge based on:
*
* - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
* - https://www.shadertoy.com/view/ldl3W8
*
* With optimization to change -2..2 scan window to -1..1 for better performance,
* as explained in https://www.shadertoy.com/view/llG3zy.
*/
/* **** 1D Voronoi **** */
vector4 voronoi_position(float coord)
{
return vector4(0.0, 0.0, 0.0, coord);
}
VoronoiOutput voronoi_f1(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float minDistance = 8.0;
float targetOffset = 0.0;
float targetPosition = 0.0;
for (int i = -1; i <= 1; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_float_to_color(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float smoothDistance = 8.0;
float smoothPosition = 0.0;
vector3 smoothColor = vector3(0.0, 0.0, 0.0);
for (int i = -2; i <= 2; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
color cellColor = hash_float_to_color(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
float offsetF1 = 0.0;
float positionF1 = 0.0;
float offsetF2 = 0.0;
float positionF2 = 0.0;
for (int i = -1; i <= 1; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_float_to_color(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float midPointPosition = hash_float_to_float(cellPosition) * params.randomness;
float leftPointPosition = -1.0 + hash_float_to_float(cellPosition - 1.0) * params.randomness;
float rightPointPosition = 1.0 + hash_float_to_float(cellPosition + 1.0) * params.randomness;
float distanceToMidLeft = abs((midPointPosition + leftPointPosition) / 2.0 - localPosition);
float distanceToMidRight = abs((midPointPosition + rightPointPosition) / 2.0 - localPosition);
return min(distanceToMidLeft, distanceToMidRight);
}
float voronoi_n_sphere_radius(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float closestPoint = 0.0;
float closestPointOffset = 0.0;
float minDistance = 8.0;
for (int i = -1; i <= 1; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = abs(pointPosition - localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
minDistance = 8.0;
float closestPointToClosestPoint = 0.0;
for (int i = -1; i <= 1; i++) {
if (i == 0) {
continue;
}
float cellOffset = i + closestPointOffset;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = abs(closestPoint - pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
return abs(closestPointToClosestPoint - closestPoint) / 2.0;
}
/* **** 2D Voronoi **** */
vector4 voronoi_position(vector2 coord)
{
return vector4(coord.x, coord.y, 0.0, 0.0);
}
VoronoiOutput voronoi_f1(VoronoiParams params, vector2 coord)
{
vector2 cellPosition = floor(coord);
vector2 localPosition = coord - cellPosition;
float minDistance = 8.0;
vector2 targetOffset = vector2(0.0, 0.0);
vector2 targetPosition = vector2(0.0, 0.0);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector2 cellOffset = vector2(i, j);
vector2 pointPosition = cellOffset + hash_vector2_to_vector2(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_vector2_to_color(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, vector2 coord)
{
vector2 cellPosition = floor(coord);
vector2 localPosition = coord - cellPosition;
float smoothDistance = 8.0;
vector3 smoothColor = vector3(0.0, 0.0, 0.0);
vector2 smoothPosition = vector2(0.0, 0.0);
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
vector2 cellOffset = vector2(i, j);
vector2 pointPosition = cellOffset + hash_vector2_to_vector2(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
color cellColor = hash_vector2_to_color(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, vector2 coord)
{
vector2 cellPosition = floor(coord);
vector2 localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
vector2 offsetF1 = vector2(0.0, 0.0);
vector2 positionF1 = vector2(0.0, 0.0);
vector2 offsetF2 = vector2(0.0, 0.0);
vector2 positionF2 = vector2(0.0, 0.0);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector2 cellOffset = vector2(i, j);
vector2 pointPosition = cellOffset + hash_vector2_to_vector2(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_vector2_to_color(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, vector2 coord)
{
vector2 cellPosition = floor(coord);
vector2 localPosition = coord - cellPosition;
vector2 vectorToClosest = vector2(0.0, 0.0);
float minDistance = 8.0;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector2 cellOffset = vector2(i, j);
vector2 vectorToPoint = cellOffset +
hash_vector2_to_vector2(cellPosition + cellOffset) *
params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
minDistance = 8.0;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector2 cellOffset = vector2(i, j);
vector2 vectorToPoint = cellOffset +
hash_vector2_to_vector2(cellPosition + cellOffset) *
params.randomness -
localPosition;
vector2 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
normalize(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
return minDistance;
}
float voronoi_n_sphere_radius(VoronoiParams params, vector2 coord)
{
vector2 cellPosition = floor(coord);
vector2 localPosition = coord - cellPosition;
vector2 closestPoint = vector2(0.0, 0.0);
vector2 closestPointOffset = vector2(0.0, 0.0);
float minDistance = 8.0;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector2 cellOffset = vector2(i, j);
vector2 pointPosition = cellOffset + hash_vector2_to_vector2(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
minDistance = 8.0;
vector2 closestPointToClosestPoint = vector2(0.0, 0.0);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0) {
continue;
}
vector2 cellOffset = vector2(i, j) + closestPointOffset;
vector2 pointPosition = cellOffset + hash_vector2_to_vector2(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0;
}
/* **** 3D Voronoi **** */
vector4 voronoi_position(vector3 coord)
{
return vector4(coord.x, coord.y, coord.z, 0.0);
}
VoronoiOutput voronoi_f1(VoronoiParams params, vector3 coord)
{
vector3 cellPosition = floor(coord);
vector3 localPosition = coord - cellPosition;
float minDistance = 8.0;
vector3 targetOffset = vector3(0.0, 0.0, 0.0);
vector3 targetPosition = vector3(0.0, 0.0, 0.0);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector3 cellOffset = vector3(i, j, k);
vector3 pointPosition = cellOffset + hash_vector3_to_vector3(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_vector3_to_color(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, vector3 coord)
{
vector3 cellPosition = floor(coord);
vector3 localPosition = coord - cellPosition;
float smoothDistance = 8.0;
vector3 smoothColor = vector3(0.0, 0.0, 0.0);
vector3 smoothPosition = vector3(0.0, 0.0, 0.0);
for (int k = -2; k <= 2; k++) {
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
vector3 cellOffset = vector3(i, j, k);
vector3 pointPosition = cellOffset + hash_vector3_to_vector3(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
color cellColor = hash_vector3_to_color(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, vector3 coord)
{
vector3 cellPosition = floor(coord);
vector3 localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
vector3 offsetF1 = vector3(0.0, 0.0, 0.0);
vector3 positionF1 = vector3(0.0, 0.0, 0.0);
vector3 offsetF2 = vector3(0.0, 0.0, 0.0);
vector3 positionF2 = vector3(0.0, 0.0, 0.0);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector3 cellOffset = vector3(i, j, k);
vector3 pointPosition = cellOffset + hash_vector3_to_vector3(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_vector3_to_color(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, vector3 coord)
{
vector3 cellPosition = floor(coord);
vector3 localPosition = coord - cellPosition;
vector3 vectorToClosest = vector3(0.0, 0.0, 0.0);
float minDistance = 8.0;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector3 cellOffset = vector3(i, j, k);
vector3 vectorToPoint = cellOffset +
hash_vector3_to_vector3(cellPosition + cellOffset) *
params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
minDistance = 8.0;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector3 cellOffset = vector3(i, j, k);
vector3 vectorToPoint = cellOffset +
hash_vector3_to_vector3(cellPosition + cellOffset) *
params.randomness -
localPosition;
vector3 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
normalize((vector)perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
}
return minDistance;
}
float voronoi_n_sphere_radius(VoronoiParams params, vector3 coord)
{
vector3 cellPosition = floor(coord);
vector3 localPosition = coord - cellPosition;
vector3 closestPoint = vector3(0.0, 0.0, 0.0);
vector3 closestPointOffset = vector3(0.0, 0.0, 0.0);
float minDistance = 8.0;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector3 cellOffset = vector3(i, j, k);
vector3 pointPosition = cellOffset + hash_vector3_to_vector3(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
minDistance = 8.0;
vector3 closestPointToClosestPoint = vector3(0.0, 0.0, 0.0);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0 && k == 0) {
continue;
}
vector3 cellOffset = vector3(i, j, k) + closestPointOffset;
vector3 pointPosition = cellOffset + hash_vector3_to_vector3(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0;
}
/* **** 4D Voronoi **** */
vector4 voronoi_position(vector4 coord)
{
return coord;
}
VoronoiOutput voronoi_f1(VoronoiParams params, vector4 coord)
{
vector4 cellPosition = floor(coord);
vector4 localPosition = coord - cellPosition;
float minDistance = 8.0;
vector4 targetOffset = vector4(0.0, 0.0, 0.0, 0.0);
vector4 targetPosition = vector4(0.0, 0.0, 0.0, 0.0);
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector4 cellOffset = vector4(i, j, k, u);
vector4 pointPosition = cellOffset + hash_vector4_to_vector4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_vector4_to_color(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, vector4 coord)
{
vector4 cellPosition = floor(coord);
vector4 localPosition = coord - cellPosition;
float smoothDistance = 8.0;
vector3 smoothColor = vector3(0.0, 0.0, 0.0);
vector4 smoothPosition = vector4(0.0, 0.0, 0.0, 0.0);
for (int u = -2; u <= 2; u++) {
for (int k = -2; k <= 2; k++) {
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
vector4 cellOffset = vector4(i, j, k, u);
vector4 pointPosition = cellOffset + hash_vector4_to_vector4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
color cellColor = hash_vector4_to_color(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
}
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, vector4 coord)
{
vector4 cellPosition = floor(coord);
vector4 localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
vector4 offsetF1 = vector4(0.0, 0.0, 0.0, 0.0);
vector4 positionF1 = vector4(0.0, 0.0, 0.0, 0.0);
vector4 offsetF2 = vector4(0.0, 0.0, 0.0, 0.0);
vector4 positionF2 = vector4(0.0, 0.0, 0.0, 0.0);
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector4 cellOffset = vector4(i, j, k, u);
vector4 pointPosition = cellOffset + hash_vector4_to_vector4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
}
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_vector4_to_color(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, vector4 coord)
{
vector4 cellPosition = floor(coord);
vector4 localPosition = coord - cellPosition;
vector4 vectorToClosest = vector4(0.0, 0.0, 0.0, 0.0);
float minDistance = 8.0;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector4 cellOffset = vector4(i, j, k, u);
vector4 vectorToPoint = cellOffset +
hash_vector4_to_vector4(cellPosition + cellOffset) *
params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
}
minDistance = 8.0;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector4 cellOffset = vector4(i, j, k, u);
vector4 vectorToPoint = cellOffset +
hash_vector4_to_vector4(cellPosition + cellOffset) *
params.randomness -
localPosition;
vector4 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
normalize(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
}
}
return minDistance;
}
float voronoi_n_sphere_radius(VoronoiParams params, vector4 coord)
{
vector4 cellPosition = floor(coord);
vector4 localPosition = coord - cellPosition;
vector4 closestPoint = vector4(0.0, 0.0, 0.0, 0.0);
vector4 closestPointOffset = vector4(0.0, 0.0, 0.0, 0.0);
float minDistance = 8.0;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vector4 cellOffset = vector4(i, j, k, u);
vector4 pointPosition = cellOffset + hash_vector4_to_vector4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
}
minDistance = 8.0;
vector4 closestPointToClosestPoint = vector4(0.0, 0.0, 0.0, 0.0);
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0 && k == 0 && u == 0) {
continue;
}
vector4 cellOffset = vector4(i, j, k, u) + closestPointOffset;
vector4 pointPosition = cellOffset + hash_vector4_to_vector4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0;
}

File diff suppressed because it is too large Load Diff

View File

@ -434,12 +434,14 @@ typedef enum ClosureType {
CLOSURE_BSDF_REFRACTION_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID,
CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID,
CLOSURE_BSDF_HAIR_PRINCIPLED_ID,
CLOSURE_BSDF_HAIR_TRANSMISSION_ID,
/* Glass */
CLOSURE_BSDF_SHARP_GLASS_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID,
CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID,
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID, /* virtual closure */
CLOSURE_BSDF_HAIR_PRINCIPLED_ID,
CLOSURE_BSDF_HAIR_TRANSMISSION_ID,
/* Special cases */
CLOSURE_BSDF_TRANSPARENT_ID,
@ -473,15 +475,15 @@ typedef enum ClosureType {
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID)
#define CLOSURE_IS_BSDF_SINGULAR(type) \
(type == CLOSURE_BSDF_REFLECTION_ID || type == CLOSURE_BSDF_REFRACTION_ID || \
type == CLOSURE_BSDF_TRANSPARENT_ID)
type == CLOSURE_BSDF_TRANSPARENT_ID || type == CLOSURE_BSDF_SHARP_GLASS_ID)
#define CLOSURE_IS_BSDF_TRANSPARENT(type) (type == CLOSURE_BSDF_TRANSPARENT_ID)
#define CLOSURE_IS_BSDF_MULTISCATTER(type) \
(type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID || \
type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_BSDF_MICROFACET(type) \
((type >= CLOSURE_BSDF_MICROFACET_GGX_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID) || \
(type >= CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID && \
type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID))
((type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID) || \
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID) || \
(type >= CLOSURE_BSDF_SHARP_GLASS_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID))
#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID)
#define CLOSURE_IS_BSSRDF(type) \
(type >= CLOSURE_BSSRDF_BURLEY_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID)
@ -491,8 +493,8 @@ typedef enum ClosureType {
#define CLOSURE_IS_VOLUME_ABSORPTION(type) (type == CLOSURE_VOLUME_ABSORPTION_ID)
#define CLOSURE_IS_HOLDOUT(type) (type == CLOSURE_HOLDOUT_ID)
#define CLOSURE_IS_PHASE(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID)
#define CLOSURE_IS_REFRACTIVE(type) \
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_REFRACTION(type) \
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID)
#define CLOSURE_IS_GLASS(type) \
(type >= CLOSURE_BSDF_SHARP_GLASS_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_PRINCIPLED(type) (type == CLOSURE_BSDF_PRINCIPLED_ID)

File diff suppressed because it is too large Load Diff

View File

@ -1207,9 +1207,14 @@ NODE_DEFINE(VoronoiTextureNode)
feature_enum.insert("n_sphere_radius", NODE_VORONOI_N_SPHERE_RADIUS);
SOCKET_ENUM(feature, "Feature", feature_enum, NODE_VORONOI_F1);
SOCKET_BOOLEAN(normalize, "Normalize", false);
SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED);
SOCKET_IN_FLOAT(w, "W", 0.0f);
SOCKET_IN_FLOAT(scale, "Scale", 5.0f);
SOCKET_IN_FLOAT(detail, "Detail", 0.0f);
SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f);
SOCKET_IN_FLOAT(lacunarity, "Lacunarity", 2.0f);
SOCKET_IN_FLOAT(smoothness, "Smoothness", 5.0f);
SOCKET_IN_FLOAT(exponent, "Exponent", 0.5f);
SOCKET_IN_FLOAT(randomness, "Randomness", 1.0f);
@ -1230,6 +1235,9 @@ void VoronoiTextureNode::compile(SVMCompiler &compiler)
ShaderInput *vector_in = input("Vector");
ShaderInput *w_in = input("W");
ShaderInput *scale_in = input("Scale");
ShaderInput *detail_in = input("Detail");
ShaderInput *roughness_in = input("Roughness");
ShaderInput *lacunarity_in = input("Lacunarity");
ShaderInput *smoothness_in = input("Smoothness");
ShaderInput *exponent_in = input("Exponent");
ShaderInput *randomness_in = input("Randomness");
@ -1243,6 +1251,9 @@ void VoronoiTextureNode::compile(SVMCompiler &compiler)
int vector_stack_offset = tex_mapping.compile_begin(compiler, vector_in);
int w_in_stack_offset = compiler.stack_assign_if_linked(w_in);
int scale_stack_offset = compiler.stack_assign_if_linked(scale_in);
int detail_stack_offset = compiler.stack_assign_if_linked(detail_in);
int roughness_stack_offset = compiler.stack_assign_if_linked(roughness_in);
int lacunarity_stack_offset = compiler.stack_assign_if_linked(lacunarity_in);
int smoothness_stack_offset = compiler.stack_assign_if_linked(smoothness_in);
int exponent_stack_offset = compiler.stack_assign_if_linked(exponent_in);
int randomness_stack_offset = compiler.stack_assign_if_linked(randomness_in);
@ -1255,19 +1266,21 @@ void VoronoiTextureNode::compile(SVMCompiler &compiler)
compiler.add_node(NODE_TEX_VORONOI, dimensions, feature, metric);
compiler.add_node(
compiler.encode_uchar4(
vector_stack_offset, w_in_stack_offset, scale_stack_offset, smoothness_stack_offset),
compiler.encode_uchar4(exponent_stack_offset,
randomness_stack_offset,
distance_stack_offset,
color_stack_offset),
compiler.encode_uchar4(position_stack_offset, w_out_stack_offset, radius_stack_offset),
__float_as_int(w));
vector_stack_offset, w_in_stack_offset, scale_stack_offset, detail_stack_offset),
compiler.encode_uchar4(roughness_stack_offset,
lacunarity_stack_offset,
smoothness_stack_offset,
exponent_stack_offset),
compiler.encode_uchar4(
randomness_stack_offset, normalize, distance_stack_offset, color_stack_offset),
compiler.encode_uchar4(position_stack_offset, w_out_stack_offset, radius_stack_offset));
compiler.add_node(__float_as_int(scale),
compiler.add_node(
__float_as_int(w), __float_as_int(scale), __float_as_int(detail), __float_as_int(roughness));
compiler.add_node(__float_as_int(lacunarity),
__float_as_int(smoothness),
__float_as_int(exponent),
__float_as_int(randomness));
tex_mapping.compile_end(compiler, vector_in, vector_stack_offset);
}
@ -1278,6 +1291,7 @@ void VoronoiTextureNode::compile(OSLCompiler &compiler)
compiler.parameter(this, "dimensions");
compiler.parameter(this, "feature");
compiler.parameter(this, "metric");
compiler.parameter(this, "normalize");
compiler.add(this, "node_voronoi_texture");
}

View File

@ -254,8 +254,12 @@ class VoronoiTextureNode : public TextureNode {
NODE_SOCKET_API(int, dimensions)
NODE_SOCKET_API(NodeVoronoiDistanceMetric, metric)
NODE_SOCKET_API(NodeVoronoiFeature, feature)
NODE_SOCKET_API(bool, normalize)
NODE_SOCKET_API(float, w)
NODE_SOCKET_API(float, scale)
NODE_SOCKET_API(float, detail)
NODE_SOCKET_API(float, roughness)
NODE_SOCKET_API(float, lacunarity)
NODE_SOCKET_API(float, exponent)
NODE_SOCKET_API(float, smoothness)
NODE_SOCKET_API(float, randomness)

View File

@ -224,6 +224,12 @@ ccl_device_inline float2 floor(const float2 a)
#endif /* !__KERNEL_METAL__ */
/* Consistent name for this would be pow, but HIP compiler crashes in name mangling. */
ccl_device_inline float2 power(float2 v, float e)
{
return make_float2(powf(v.x, e), powf(v.y, e));
}
ccl_device_inline float2 safe_divide_float2_float(const float2 a, const float b)
{
return (b != 0.0f) ? a / b : zero_float2();

View File

@ -469,7 +469,8 @@ ccl_device_inline bool isequal(const float3 a, const float3 b)
#endif
}
ccl_device_inline float3 pow(float3 v, float e)
/* Consistent name for this would be pow, but HIP compiler crashes in name mangling. */
ccl_device_inline float3 power(float3 v, float e)
{
return make_float3(powf(v.x, e), powf(v.y, e), powf(v.z, e));
}

View File

@ -593,7 +593,8 @@ ccl_device_inline float4 ensure_finite(float4 v)
return v;
}
ccl_device_inline float4 pow(float4 v, float e)
/* Consistent name for this would be pow, but HIP compiler crashes in name mangling. */
ccl_device_inline float4 power(float4 v, float e)
{
return make_float4(powf(v.x, e), powf(v.y, e), powf(v.z, e), powf(v.w, e));
}

View File

@ -44,10 +44,9 @@ def geometry_modifier_poll(context):
def get_context_modifier(context):
# Context only has a 'modifier' attribute in the modifier extra operators dropdown.
if hasattr(context, 'modifier'):
modifier = context.modifier
else:
# Context only has a "modifier" attribute in the modifier extra operators drop-down.
modifier = getattr(context, "modifier", ...)
if modifier is ...:
ob = context.object
if ob is None:
return False

View File

@ -2034,6 +2034,15 @@ class VIEW3D_MT_select_edit_gpencil(Menu):
layout.separator()
op = layout.operator("grease_pencil.select_ends", text="First")
op.amount_start = 1
op.amount_end = 0
op = layout.operator("grease_pencil.select_ends", text="Last")
op.amount_start = 0
op.amount_end = 1
layout.separator()
layout.operator("grease_pencil.select_more")
layout.operator("grease_pencil.select_less")

View File

@ -30,7 +30,7 @@ bool BKE_curves_attribute_required(const struct Curves *curves, const char *name
/* Depsgraph */
struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src);
struct Curves *BKE_curves_copy_for_eval(const struct Curves *curves_src);
void BKE_curves_data_update(struct Depsgraph *depsgraph,
struct Scene *scene,

View File

@ -80,7 +80,7 @@ bool BKE_pointcloud_attribute_required(const struct PointCloud *pointcloud, cons
/* Dependency Graph */
struct PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src);
struct PointCloud *BKE_pointcloud_copy_for_eval(const struct PointCloud *pointcloud_src);
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph,
struct Scene *scene,

View File

@ -48,7 +48,7 @@ struct wmWindow;
struct wmWindowManager;
/* spacetype has everything stored to get an editor working, it gets initialized via
* #ED_spacetypes_init() in `editors/space_api/spacetypes.c` */
* #ED_spacetypes_init() in `editors/space_api/spacetypes.cc` */
/* an editor in Blender is a combined ScrArea + SpaceType + SpaceData */
#define BKE_ST_MAXNAME 64

View File

@ -132,7 +132,7 @@ void BKE_volume_grid_transform_matrix_set(const struct Volume *volume,
* file path. Grids are shared with the source data-block, not copied. */
struct Volume *BKE_volume_new_for_eval(const struct Volume *volume_src);
struct Volume *BKE_volume_copy_for_eval(struct Volume *volume_src);
struct Volume *BKE_volume_copy_for_eval(const struct Volume *volume_src);
struct VolumeGrid *BKE_volume_grid_add(struct Volume *volume,
const char *name,

View File

@ -222,7 +222,7 @@ bool BKE_curves_attribute_required(const Curves * /*curves*/, const char *name)
return STREQ(name, ATTR_POSITION);
}
Curves *BKE_curves_copy_for_eval(Curves *curves_src)
Curves *BKE_curves_copy_for_eval(const Curves *curves_src)
{
return reinterpret_cast<Curves *>(
BKE_id_copy_ex(nullptr, &curves_src->id, nullptr, LIB_ID_COPY_LOCALIZE));

View File

@ -1543,6 +1543,20 @@ static void layerInterp_propbool(const void **sources,
*(bool *)dest = result;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#math::Quaternion, #CD_PROP_QUATERNION)
* \{ */
static void layerDefault_propquaternion(void *data, const int count)
{
using namespace blender;
MutableSpan(static_cast<math::Quaternion *>(data), count).fill(math::Quaternion::identity());
}
/** \} */
static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 0: CD_MVERT */ /* DEPRECATED */
{sizeof(MVert), "MVert", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
@ -1935,7 +1949,15 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 51: CD_HAIRLENGTH */
{sizeof(float), "float", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 52: CD_PROP_QUATERNION */
{sizeof(float[4]), "vec4f", 1, N_("Quaternion"), nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float[4]),
"vec4f",
1,
N_("Quaternion"),
nullptr,
nullptr,
nullptr,
nullptr,
layerDefault_propquaternion},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@ -1993,6 +2015,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDPropFloat2",
"CDPropBoolean",
"CDHairLength",
"CDPropQuaternion",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {

View File

@ -11,6 +11,7 @@
#include "DNA_object_types.h"
#include "BLI_map.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BLI_timeit.hh"
@ -21,43 +22,14 @@
namespace blender::bke::calc_edges {
/** This is used to uniquely identify edges in a hash map. */
struct OrderedEdge {
int v_low, v_high;
OrderedEdge(const int v1, const int v2)
{
if (v1 < v2) {
v_low = v1;
v_high = v2;
}
else {
v_low = v2;
v_high = v1;
}
}
OrderedEdge(const uint v1, const uint v2) : OrderedEdge(int(v1), int(v2)) {}
uint64_t hash() const
{
return (this->v_low << 8) ^ this->v_high;
}
/** Return a hash value that is likely to be different in the low bits from the normal `hash()`
* function. This is necessary to avoid collisions in #BKE_mesh_calc_edges. */
uint64_t hash2() const
{
return this->v_low;
}
friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
{
BLI_assert(e1.v_low < e1.v_high);
BLI_assert(e2.v_low < e2.v_high);
return e1.v_low == e2.v_low && e1.v_high == e2.v_high;
}
};
/**
* Return a hash value that is likely to be different in the low bits from the normal `hash()`
* function. This is necessary to avoid collisions in #BKE_mesh_calc_edges.
*/
static uint64_t edge_hash_2(const OrderedEdge &edge)
{
return edge.v_low;
}
/* The map first contains an edge pointer and later an index. */
union OrigEdgeOrIndex {
@ -86,7 +58,7 @@ static void add_existing_edges_to_hash_maps(Mesh *mesh,
for (const int2 &edge : edges) {
OrderedEdge ordered_edge{edge[0], edge[1]};
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & ordered_edge.hash2())) {
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.add_new(ordered_edge, {&edge});
}
}
@ -109,7 +81,7 @@ static void add_polygon_edges_to_hash_maps(Mesh *mesh,
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & ordered_edge.hash2())) {
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.lookup_or_add(ordered_edge, {nullptr});
}
}
@ -171,7 +143,7 @@ static void update_edge_indices_in_poly_loops(const OffsetIndices<int> polys,
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
/* Double lookup: First find the map that contains the edge, then lookup the edge. */
const EdgeMap &edge_map = edge_maps[parallel_mask & ordered_edge.hash2()];
const EdgeMap &edge_map = edge_maps[parallel_mask & edge_hash_2(ordered_edge)];
edge_index = edge_map.lookup(ordered_edge).index;
}
else {

View File

@ -320,7 +320,7 @@ bool BKE_pointcloud_attribute_required(const PointCloud * /*pointcloud*/, const
/* Dependency Graph */
PointCloud *BKE_pointcloud_copy_for_eval(PointCloud *pointcloud_src)
PointCloud *BKE_pointcloud_copy_for_eval(const PointCloud *pointcloud_src)
{
return reinterpret_cast<PointCloud *>(
BKE_id_copy_ex(nullptr, &pointcloud_src->id, nullptr, LIB_ID_COPY_LOCALIZE));

View File

@ -2684,7 +2684,8 @@ void BKE_scene_update_sound(Depsgraph *depsgraph, Main *bmain)
BKE_sound_set_scene_volume(scene, scene->audio.volume);
}
if (recalc & ID_RECALC_AUDIO_MUTE) {
const bool is_mute = (scene->audio.flag & AUDIO_MUTE);
const bool is_mute = (DEG_get_mode(depsgraph) == DAG_EVAL_VIEWPORT) &&
(scene->audio.flag & AUDIO_MUTE);
BKE_sound_mute_scene(scene, is_mute);
}
if (recalc & ID_RECALC_AUDIO_LISTENER) {

View File

@ -1530,7 +1530,7 @@ Volume *BKE_volume_new_for_eval(const Volume *volume_src)
return volume_dst;
}
Volume *BKE_volume_copy_for_eval(Volume *volume_src)
Volume *BKE_volume_copy_for_eval(const Volume *volume_src)
{
return reinterpret_cast<Volume *>(
BKE_id_copy_ex(nullptr, &volume_src->id, nullptr, LIB_ID_COPY_LOCALIZE));

View File

@ -306,87 +306,85 @@ float musgrave_hetero_terrain(
/** \name Voronoi Noise
* \{ */
void voronoi_f1(float w, float randomness, float *r_distance, float3 *r_color, float *r_w);
void voronoi_smooth_f1(
float w, float smoothness, float randomness, float *r_distance, float3 *r_color, float *r_w);
void voronoi_f2(float w, float randomness, float *r_distance, float3 *r_color, float *r_w);
void voronoi_distance_to_edge(float w, float randomness, float *r_distance);
void voronoi_n_sphere_radius(float w, float randomness, float *r_radius);
struct VoronoiParams {
float scale;
float detail;
float roughness;
float lacunarity;
float smoothness;
float exponent;
float randomness;
float max_distance;
bool normalize;
int feature;
int metric;
};
void voronoi_f1(const float2 coord,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float2 *r_position);
void voronoi_smooth_f1(const float2 coord,
float smoothness,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float2 *r_position);
void voronoi_f2(const float2 coord,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float2 *r_position);
void voronoi_distance_to_edge(const float2 coord, float randomness, float *r_distance);
void voronoi_n_sphere_radius(const float2 coord, float randomness, float *r_radius);
struct VoronoiOutput {
float distance = 0.0f;
float3 color{0.0f, 0.0f, 0.0f};
float4 position{0.0f, 0.0f, 0.0f, 0.0f};
};
void voronoi_f1(const float3 coord,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float3 *r_position);
void voronoi_smooth_f1(const float3 coord,
float smoothness,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float3 *r_position);
void voronoi_f2(const float3 coord,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float3 *r_position);
void voronoi_distance_to_edge(const float3 coord, float randomness, float *r_distance);
void voronoi_n_sphere_radius(const float3 coord, float randomness, float *r_radius);
/* ***** Distances ***** */
void voronoi_f1(const float4 coord,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float4 *r_position);
void voronoi_smooth_f1(const float4 coord,
float smoothness,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float4 *r_position);
void voronoi_f2(const float4 coord,
float exponent,
float randomness,
int metric,
float *r_distance,
float3 *r_color,
float4 *r_position);
void voronoi_distance_to_edge(const float4 coord, float randomness, float *r_distance);
void voronoi_n_sphere_radius(const float4 coord, float randomness, float *r_radius);
float voronoi_distance(const float a, const float b);
float voronoi_distance(const float2 a, const float2 b, const VoronoiParams &params);
float voronoi_distance(const float3 a, const float3 b, const VoronoiParams &params);
float voronoi_distance(const float4 a, const float4 b, const VoronoiParams &params);
/* **** 1D Voronoi **** */
float4 voronoi_position(const float coord);
VoronoiOutput voronoi_f1(const VoronoiParams &params, const float coord);
VoronoiOutput voronoi_smooth_f1(const VoronoiParams &params,
const float coord,
const bool calc_color);
VoronoiOutput voronoi_f2(const VoronoiParams &params, const float coord);
float voronoi_distance_to_edge(const VoronoiParams &params, const float coord);
float voronoi_n_sphere_radius(const VoronoiParams &params, const float coord);
/* **** 2D Voronoi **** */
float4 voronoi_position(const float2 coord);
VoronoiOutput voronoi_f1(const VoronoiParams &params, const float2 coord);
VoronoiOutput voronoi_smooth_f1(const VoronoiParams &params,
const float2 coord,
const bool calc_color);
VoronoiOutput voronoi_f2(const VoronoiParams &params, const float2 coord);
float voronoi_distance_to_edge(const VoronoiParams &params, const float2 coord);
float voronoi_n_sphere_radius(const VoronoiParams &params, const float2 coord);
/* **** 3D Voronoi **** */
float4 voronoi_position(const float3 coord);
VoronoiOutput voronoi_f1(const VoronoiParams &params, const float3 coord);
VoronoiOutput voronoi_smooth_f1(const VoronoiParams &params,
const float3 coord,
const bool calc_color);
VoronoiOutput voronoi_f2(const VoronoiParams &params, const float3 coord);
float voronoi_distance_to_edge(const VoronoiParams &params, const float3 coord);
float voronoi_n_sphere_radius(const VoronoiParams &params, const float3 coord);
/* **** 4D Voronoi **** */
float4 voronoi_position(const float4 coord);
VoronoiOutput voronoi_f1(const VoronoiParams &params, const float4 coord);
VoronoiOutput voronoi_smooth_f1(const VoronoiParams &params,
const float4 coord,
const bool calc_color);
VoronoiOutput voronoi_f2(const VoronoiParams &params, const float4 coord);
float voronoi_distance_to_edge(const VoronoiParams &params, const float4 coord);
float voronoi_n_sphere_radius(const VoronoiParams &params, const float4 coord);
/* Fractal Voronoi Noise */
template<typename T>
VoronoiOutput fractal_voronoi_x_fx(const VoronoiParams &params,
const T coord,
const bool calc_color);
template<typename T>
float fractal_voronoi_distance_to_edge(const VoronoiParams &params, const T coord);
/** \} */

View File

@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_assert.h"
#include "BLI_math_vector_types.hh"
namespace blender {
/**
* A version of `int2` used as a key for hash-maps, agnostic of the arbitrary order of the two
* vertices in a mesh edge.
*/
struct OrderedEdge {
int v_low;
int v_high;
OrderedEdge(const int v1, const int v2)
{
if (v1 < v2) {
v_low = v1;
v_high = v2;
}
else {
v_low = v2;
v_high = v1;
}
}
OrderedEdge(const int2 edge) : OrderedEdge(edge[0], edge[1]) {}
OrderedEdge(const uint v1, const uint v2) : OrderedEdge(int(v1), int(v2)) {}
uint64_t hash() const
{
return (this->v_low << 8) ^ this->v_high;
}
friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
{
BLI_assert(e1.v_low < e1.v_high);
BLI_assert(e2.v_low < e2.v_high);
return e1.v_low == e2.v_low && e1.v_high == e2.v_high;
}
};
} // namespace blender

View File

@ -265,6 +265,10 @@ int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
* Removes the last slash and everything after it to the end of path, if there is one.
*/
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1);
/**
* \return the next non-slash character or the null byte (when `path` only contains slashes).
*/
const char *BLI_path_slash_skip(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Changes to the path separators to the native ones for this OS.
*/

View File

@ -320,6 +320,7 @@ set(SRC
BLI_noise.hh
BLI_offset_indices.hh
BLI_offset_span.hh
BLI_ordered_edge.hh
BLI_parameter_pack_utils.hh
BLI_path_util.h
BLI_polyfill_2d.h

File diff suppressed because it is too large Load Diff

View File

@ -1118,9 +1118,7 @@ bool BLI_path_abs(char path[FILE_MAX], const char *basepath)
BLI_assert(strlen(tmp) == root_dir_len);
/* Step over the slashes at the beginning of the path. */
while (BLI_path_slash_is_native_compat(*p)) {
p++;
}
p = (char *)BLI_path_slash_skip(p);
BLI_strncpy(tmp + root_dir_len, p, sizeof(tmp) - root_dir_len);
}
else {
@ -1958,6 +1956,15 @@ void BLI_path_slash_rstrip(char *path)
}
}
const char *BLI_path_slash_skip(const char *path)
{
/* This accounts for a null byte too. */
while (BLI_path_slash_is_native_compat(*path)) {
path++;
}
return path;
}
void BLI_path_slash_native(char *path)
{
#ifdef WIN32

View File

@ -42,7 +42,7 @@ set(SRC
intern/undofile.cc
intern/versioning_250.c
intern/versioning_260.c
intern/versioning_270.c
intern/versioning_270.cc
intern/versioning_280.cc
intern/versioning_290.cc
intern/versioning_300.cc

View File

@ -329,7 +329,9 @@ static void do_version_scene_collection_convert(
BLI_ghash_insert(collection_map, collection, sc);
}
LISTBASE_FOREACH (SceneCollection *, nsc, &sc->scene_collections) {
for (SceneCollection *nsc = static_cast<SceneCollection *>(sc->scene_collections.first);
nsc != nullptr;)
{
SceneCollection *nsc_next = nsc->next;
Collection *ncollection = BKE_collection_add(bmain, collection, nsc->name);
ncollection->id.lib = id->lib;

View File

@ -211,6 +211,9 @@ ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph)
Object *DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
{
if (object == nullptr) {
return nullptr;
}
return (Object *)DEG_get_evaluated_id(depsgraph, &object->id);
}

View File

@ -127,7 +127,7 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph)
op_node->custom_flags = 0;
op_node->num_links_pending = 0;
for (Relation *rel : op_node->outlinks) {
if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
if ((rel->to->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
++op_node->num_links_pending;
}
}

View File

@ -172,6 +172,58 @@ static void GREASE_PENCIL_OT_select_linked(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int select_ends_exec(bContext *C, wmOperator *op)
{
const int amount_start = RNA_int_get(op->ptr, "amount_start");
const int amount_end = RNA_int_get(op->ptr, "amount_end");
Scene *scene = CTX_data_scene(C);
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int /*drawing_index*/, GreasePencilDrawing &drawing) {
blender::ed::curves::select_ends(drawing.geometry.wrap(), amount_start, amount_end);
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
return OPERATOR_FINISHED;
}
static void GREASE_PENCIL_OT_select_ends(wmOperatorType *ot)
{
ot->name = "Select Ends";
ot->idname = "GREASE_PENCIL_OT_select_ends";
ot->description = "Select end points of strokes";
ot->exec = select_ends_exec;
ot->poll = editable_grease_pencil_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_int(ot->srna,
"amount_start",
0,
0,
INT32_MAX,
"Amount Start",
"Number of points to select from the start",
0,
INT32_MAX);
RNA_def_int(ot->srna,
"amount_end",
1,
0,
INT32_MAX,
"Amount End",
"Number of points to select from the end",
0,
INT32_MAX);
}
static void keymap_grease_pencil_editing(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Edit Mode", 0, 0);
@ -187,6 +239,7 @@ void ED_operatortypes_grease_pencil(void)
WM_operatortype_append(GREASE_PENCIL_OT_select_more);
WM_operatortype_append(GREASE_PENCIL_OT_select_less);
WM_operatortype_append(GREASE_PENCIL_OT_select_linked);
WM_operatortype_append(GREASE_PENCIL_OT_select_ends);
}
void ED_keymap_grease_pencil(wmKeyConfig *keyconf)

View File

@ -10,16 +10,16 @@
#include "BLI_compiler_attrs.h"
#include "BKE_scene.h"
#ifdef __cplusplus
extern "C" {
#endif
enum eSceneCopyMethod;
struct Scene *ED_scene_add(struct Main *bmain,
struct bContext *C,
struct wmWindow *win,
enum eSceneCopyMethod method) ATTR_NONNULL();
eSceneCopyMethod method) ATTR_NONNULL();
/**
* Add a new scene in the sequence editor.
*
@ -27,8 +27,8 @@ struct Scene *ED_scene_add(struct Main *bmain,
*/
struct Scene *ED_scene_sequencer_add(struct Main *bmain,
struct bContext *C,
enum eSceneCopyMethod method,
const bool assign_strip);
eSceneCopyMethod method,
bool assign_strip);
/**
* \note Only call outside of area/region loops.
* \return true if successful.

View File

@ -493,9 +493,9 @@ bScreen *ED_screen_animation_playing(const struct wmWindowManager *wm);
bScreen *ED_screen_animation_no_scrub(const struct wmWindowManager *wm);
/* screen keymaps */
/* called in spacetypes.c */
/* called in spacetypes.cc */
void ED_operatortypes_screen(void);
/* called in spacetypes.c */
/* called in spacetypes.cc */
void ED_keymap_screen(struct wmKeyConfig *keyconf);
/**
* Workspace key-maps.

View File

@ -45,7 +45,7 @@ set(SRC
io_collada.hh
io_gpencil.hh
io_obj.hh
io_ops.h
io_ops.hh
io_ply_ops.hh
io_stl_ops.hh
io_usd.hh

View File

@ -6,7 +6,7 @@
* \ingroup collada
*/
#include "io_ops.h" /* own include */
#include "io_ops.hh" /* own include */
#include "WM_api.h"
@ -28,7 +28,7 @@
#include "io_ply_ops.hh"
#include "io_stl_ops.hh"
void ED_operatortypes_io(void)
void ED_operatortypes_io()
{
#ifdef WITH_COLLADA
/* Collada operators: */

View File

@ -8,12 +8,4 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void ED_operatortypes_io(void);
#ifdef __cplusplus
}
#endif
void ED_operatortypes_io();

View File

@ -5,6 +5,7 @@
set(INC
../include
../io
../../asset_system
../../blenkernel
../../blenlib
../../gpu
@ -19,7 +20,7 @@ set(INC_SYS
)
set(SRC
spacetypes.c
spacetypes.cc
)
set(LIB

View File

@ -6,7 +6,7 @@
* \ingroup spapi
*/
#include <stdlib.h>
#include <cstdlib>
#include "MEM_guardedalloc.h"
@ -58,9 +58,9 @@
#include "ED_util.h"
#include "ED_uvedit.h"
#include "io_ops.h"
#include "io_ops.hh"
void ED_spacetypes_init(void)
void ED_spacetypes_init()
{
/* UI unit is a variable, may be used in some space type initialization. */
U.widget_unit = 20;
@ -149,7 +149,7 @@ void ED_spacetypes_init(void)
}
}
void ED_spacemacros_init(void)
void ED_spacemacros_init()
{
/* Macros must go last since they reference other operators.
* They need to be registered after python operators too. */
@ -221,22 +221,21 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf)
/* ********************** Custom Draw Call API ***************** */
typedef struct RegionDrawCB {
struct RegionDrawCB {
struct RegionDrawCB *next, *prev;
void (*draw)(const bContext *, ARegion *, void *);
void *customdata;
int type;
} RegionDrawCB;
};
void *ED_region_draw_cb_activate(ARegionType *art,
void (*draw)(const bContext *, ARegion *, void *),
void *customdata,
int type)
{
RegionDrawCB *rdc = MEM_callocN(sizeof(RegionDrawCB), "RegionDrawCB");
RegionDrawCB *rdc = MEM_cnew<RegionDrawCB>(__func__);
BLI_addtail(&art->drawcalls, rdc);
rdc->draw = draw;
@ -277,7 +276,7 @@ void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type)
void ED_region_surface_draw_cb_draw(ARegionType *art, int type)
{
ed_region_draw_cb_draw(NULL, NULL, art, type);
ed_region_draw_cb_draw(nullptr, nullptr, art, type);
}
void ED_region_draw_cb_remove_by_type(ARegionType *art, void *draw_fn, void (*free)(void *))
@ -295,19 +294,19 @@ void ED_region_draw_cb_remove_by_type(ARegionType *art, void *draw_fn, void (*fr
/* ********************* space template *********************** */
/* forward declare */
void ED_spacetype_xxx(void);
void ED_spacetype_xxx();
/* allocate and init some vars */
static SpaceLink *xxx_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
static SpaceLink *xxx_create(const ScrArea * /*area*/, const Scene * /*scene*/)
{
return NULL;
return nullptr;
}
/* Doesn't free the space-link itself. */
static void xxx_free(SpaceLink *UNUSED(sl)) {}
static void xxx_free(SpaceLink * /*sl*/) {}
/* spacetype; init callback for usage, should be re-doable. */
static void xxx_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area))
static void xxx_init(wmWindowManager * /*wm*/, ScrArea * /*area*/)
{
/* link area to SpaceXXX struct */
@ -317,24 +316,23 @@ static void xxx_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area))
/* add types to regions */
}
static SpaceLink *xxx_duplicate(SpaceLink *UNUSED(sl))
static SpaceLink *xxx_duplicate(SpaceLink * /*sl*/)
{
return NULL;
return nullptr;
}
static void xxx_operatortypes(void)
static void xxx_operatortypes()
{
/* register operator types for this space */
}
static void xxx_keymap(wmKeyConfig *UNUSED(keyconf))
static void xxx_keymap(wmKeyConfig * /*keyconf*/)
{
/* add default items to keymap */
}
/* only called once, from screen/spacetypes.c */
void ED_spacetype_xxx(void)
/* only called once, from screen/spacetypes.cc */
void ED_spacetype_xxx()
{
static SpaceType st;

View File

@ -405,8 +405,8 @@ std::string AssetCatalogDropTarget::drop_tooltip_asset_catalog(const wmDrag &dra
const AssetCatalog *src_catalog = get_drag_catalog(drag, get_asset_library());
return fmt::format(TIP_("Move catalog {} into {}"),
(std::string_view)src_catalog->path.name(),
(std::string_view)catalog_item_.get_name());
std::string_view(src_catalog->path.name()),
std::string_view(catalog_item_.get_name()));
}
std::string AssetCatalogDropTarget::drop_tooltip_asset_list(const wmDrag &drag) const
@ -620,7 +620,7 @@ std::string AssetCatalogTreeViewAllItem::DropTarget::drop_tooltip(const wmDrag &
drag, *get_view<AssetCatalogTreeView>().asset_library_);
return fmt::format(TIP_("Move catalog {} to the top level of the tree"),
(std::string_view)drag_catalog->path.name());
std::string_view(drag_catalog->path.name()));
}
bool AssetCatalogTreeViewAllItem::DropTarget::on_drop(bContext * /*C*/, const wmDrag &drag) const

View File

@ -58,6 +58,7 @@ set(SRC
tree/tree_element_gpencil_layer.cc
tree/tree_element_id.cc
tree/tree_element_id_curve.cc
tree/tree_element_id_gpencil_legacy.cc
tree/tree_element_id_library.cc
tree/tree_element_id_linestyle.cc
tree/tree_element_id_mesh.cc
@ -83,6 +84,7 @@ set(SRC
tree/tree_element_gpencil_layer.hh
tree/tree_element_id.hh
tree/tree_element_id_curve.hh
tree/tree_element_id_gpencil_legacy.hh
tree/tree_element_id_library.hh
tree/tree_element_id_linestyle.hh
tree/tree_element_id_mesh.hh

View File

@ -556,6 +556,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
case ID_MB:
case ID_TE:
case ID_LS:
case ID_GD_LEGACY:
BLI_assert_msg(0, "ID type expected to be expanded through new tree-element design");
break;
case ID_OB: {
@ -673,21 +674,6 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
}
break;
}
case ID_GD_LEGACY: {
bGPdata *gpd = (bGPdata *)id;
if (outliner_animdata_test(gpd->adt)) {
outliner_add_element(space_outliner, &te->subtree, gpd, te, TSE_ANIM_DATA, 0);
}
/* TODO: base element for layers? */
int index = 0;
LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl, &gpd->layers) {
outliner_add_element(space_outliner, &te->subtree, gpl, te, TSE_GP_LAYER, index);
index++;
}
break;
}
case ID_GR: {
/* Don't expand for instances, creates too many elements. */
if (!(te->parent && te->parent->idcode == ID_OB)) {

View File

@ -21,6 +21,7 @@
#include "../outliner_intern.hh"
#include "common.hh"
#include "tree_element_id_curve.hh"
#include "tree_element_id_gpencil_legacy.hh"
#include "tree_element_id_library.hh"
#include "tree_element_id_linestyle.hh"
#include "tree_element_id_mesh.hh"
@ -54,6 +55,8 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
return std::make_unique<TreeElementIDTexture>(legacy_te, (Tex &)id);
case ID_LS:
return std::make_unique<TreeElementIDLineStyle>(legacy_te, (FreestyleLineStyle &)id);
case ID_GD_LEGACY:
return std::make_unique<TreeElementIDGPLegacy>(legacy_te, (bGPdata &)id);
case ID_OB:
case ID_MA:
case ID_LT:
@ -70,7 +73,6 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
case ID_MC:
case ID_MSK:
case ID_LP:
case ID_GD_LEGACY:
case ID_WS:
case ID_CV:
case ID_PT:

View File

@ -0,0 +1,49 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spoutliner
*/
#include "DNA_ID.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_listBase.h"
#include "DNA_outliner_types.h"
#include "BLI_listbase.h"
#include "../outliner_intern.hh"
#include "tree_element_id_gpencil_legacy.hh"
namespace blender::ed::outliner {
TreeElementIDGPLegacy::TreeElementIDGPLegacy(TreeElement &legacy_te, bGPdata &gpd)
: TreeElementID(legacy_te, gpd.id), gpd_(gpd)
{
}
void TreeElementIDGPLegacy::expand(SpaceOutliner &space_outliner) const
{
expand_animation_data(space_outliner, gpd_.adt);
expandLayers(space_outliner);
}
bool TreeElementIDGPLegacy::isExpandValid() const
{
return true;
}
void TreeElementIDGPLegacy::expandLayers(SpaceOutliner &space_outliner) const
{
int index = 0;
LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl, &gpd_.layers) {
outliner_add_element(
&space_outliner, &legacy_te_.subtree, gpl, &legacy_te_, TSE_GP_LAYER, index);
index++;
}
}
} // namespace blender::ed::outliner

View File

@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spoutliner
*/
#pragma once
#include "tree_element_id.hh"
namespace blender::ed::outliner {
class TreeElementIDGPLegacy final : public TreeElementID {
bGPdata &gpd_;
public:
TreeElementIDGPLegacy(TreeElement &legacy_te, bGPdata &gpd);
void expand(SpaceOutliner &) const override;
bool isExpandValid() const override;
private:
void expandLayers(SpaceOutliner &) const;
};
} // namespace blender::ed::outliner

View File

@ -40,8 +40,12 @@ void TreeElementIDLineStyle::expandTextures(SpaceOutliner &space_outliner) const
{
for (int a = 0; a < MAX_MTEX; a++) {
if (linestyle_.mtex[a]) {
outliner_add_element(
&space_outliner, &legacy_te_.subtree, (linestyle_.mtex[a])->tex, &legacy_te_, TSE_SOME_ID, a);
outliner_add_element(&space_outliner,
&legacy_te_.subtree,
(linestyle_.mtex[a])->tex,
&legacy_te_,
TSE_SOME_ID,
a);
}
}
}

View File

@ -549,7 +549,7 @@ void SEQUENCER_OT_retiming_segment_speed_set(wmOperatorType *ot)
"Speed",
"New speed of retimed segment",
0.1f,
INT_MAX);
FLT_MAX);
}
/** \} */

View File

@ -101,6 +101,8 @@ static eV3D_OpEvent view3d_navigate_event(ViewOpsData *vod, const wmEvent *event
{
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case VIEW_MODAL_CANCEL:
return VIEW_CANCEL;
case VIEW_MODAL_CONFIRM:
return VIEW_CONFIRM;
case VIEWROT_MODAL_AXIS_SNAP_ENABLE:

View File

@ -1457,6 +1457,8 @@ static void customdata_weld(
int src_i, dest_i;
int j;
int vs_flag = 0;
/* interpolates a layer at a time */
dest_i = 0;
for (src_i = 0; src_i < source->totlayer; src_i++) {
@ -1477,7 +1479,21 @@ static void customdata_weld(
/* if we found a matching layer, add the data */
if (dest->layers[dest_i].type == type) {
void *src_data = source->layers[src_i].data;
if (CustomData_layer_has_interp(dest, dest_i)) {
if (type == CD_MVERT_SKIN) {
/* The `typeInfo->interp` of #CD_MVERT_SKIN does not include the flags, so #MVERT_SKIN_ROOT
* and #MVERT_SKIN_LOOSE are lost after the interpolation.
*
* This behavior is not incorrect. Ideally, islands should be checked to avoid repeated
* roots.
*
* However, for now, to prevent the loss of flags, they are simply re-added if any of the
* merged vertices have them. */
for (j = 0; j < count; j++) {
MVertSkin *vs = &((MVertSkin *)src_data)[src_indices[j]];
vs_flag |= vs->flag;
}
}
else if (CustomData_layer_has_interp(dest, dest_i)) {
/* Already calculated.
* TODO: Optimize by exposing `typeInfo->interp`. */
}
@ -1507,7 +1523,11 @@ static void customdata_weld(
for (dest_i = 0; dest_i < dest->totlayer; dest_i++) {
CustomDataLayer *layer_dst = &dest->layers[dest_i];
const eCustomDataType type = eCustomDataType(layer_dst->type);
if (CustomData_layer_has_interp(dest, dest_i)) {
if (type == CD_MVERT_SKIN) {
MVertSkin *vs = &((MVertSkin *)layer_dst->data)[dest_index];
vs->flag = vs_flag;
}
else if (CustomData_layer_has_interp(dest, dest_i)) {
/* Already calculated. */
}
else if (CustomData_layer_has_math(dest, dest_i)) {

View File

@ -4,6 +4,8 @@
#include "BLI_array_utils.hh"
#include "BLI_index_mask.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
@ -14,12 +16,6 @@
namespace blender::geometry {
/* Naively checks if the first vertices and the second vertices are the same. */
static inline bool naive_edges_equal(const int2 &edge1, const int2 &edge2)
{
return edge1 == edge2;
}
static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
{
/* These types aren't supported for interpolation below. */
@ -154,70 +150,6 @@ static void add_new_edges(Mesh &mesh,
}
}
/**
* Merge the new_edge into the original edge.
*
* NOTE: This function is very specific to the situation and makes a lot of assumptions.
*/
static void merge_edges(const int orig_edge_i,
const int new_edge_i,
MutableSpan<int> new_corner_edges,
Vector<Vector<int>> &edge_to_loop_map,
Vector<int2> &new_edges,
Vector<int> &new_to_old_edges_map)
{
/* Merge back into the original edge by undoing the topology changes. */
BLI_assert(edge_to_loop_map[new_edge_i].size() == 1);
const int loop_i = edge_to_loop_map[new_edge_i][0];
new_corner_edges[loop_i] = orig_edge_i;
/* We are putting the last edge in the location of new_edge in all the maps, to remove
* new_edge efficiently. We have to update the topology information for this last edge
* though. Essentially we are replacing every instance of last_edge_i with new_edge_i. */
const int last_edge_i = new_edges.size() - 1;
if (last_edge_i != new_edge_i) {
BLI_assert(edge_to_loop_map[last_edge_i].size() == 1);
const int last_edge_loop_i = edge_to_loop_map[last_edge_i][0];
new_corner_edges[last_edge_loop_i] = new_edge_i;
}
/* We can now safely swap-remove. */
new_edges.remove_and_reorder(new_edge_i);
edge_to_loop_map.remove_and_reorder(new_edge_i);
new_to_old_edges_map.remove_and_reorder(new_edge_i);
}
/**
* Replace the vertex of an edge with a new one, and update the connected loops.
*
* NOTE: This only updates the loops containing the edge and the old vertex. It should therefore
* also be called on the adjacent edge.
*/
static void swap_vertex_of_edge(int2 &edge,
const int old_vert,
const int new_vert,
MutableSpan<int> corner_verts,
const Span<int> connected_loops)
{
if (edge[0] == old_vert) {
edge[0] = new_vert;
}
else if (edge[1] == old_vert) {
edge[1] = new_vert;
}
else {
BLI_assert_unreachable();
}
for (const int loop_i : connected_loops) {
if (corner_verts[loop_i] == old_vert) {
corner_verts[loop_i] = new_vert;
}
/* The old vertex is on the loop containing the adjacent edge. Since this function is also
* called on the adjacent edge, we don't replace it here. */
}
}
/** Split the vertex into duplicates so that each fan has a different vertex. */
static void split_vertex_per_fan(const int vertex,
const int start_offset,
@ -225,7 +157,6 @@ static void split_vertex_per_fan(const int vertex,
const Span<int> fans,
const Span<int> fan_sizes,
const Span<Vector<int>> edge_to_loop_map,
MutableSpan<int2> new_edges,
MutableSpan<int> corner_verts,
MutableSpan<int> new_to_old_verts_map)
{
@ -237,8 +168,13 @@ static void split_vertex_per_fan(const int vertex,
new_to_old_verts_map[new_vert_i - orig_verts_num] = vertex;
for (const int edge_i : fans.slice(fan_start, fan_sizes[i])) {
swap_vertex_of_edge(
new_edges[edge_i], vertex, new_vert_i, corner_verts, edge_to_loop_map[edge_i]);
for (const int loop_i : edge_to_loop_map[edge_i]) {
if (corner_verts[loop_i] == vertex) {
corner_verts[loop_i] = new_vert_i;
}
/* The old vertex is on the loop containing the adjacent edge. Since this function is also
* called on the adjacent edge, we don't replace it here. */
}
}
fan_start += fan_sizes[i];
}
@ -267,7 +203,7 @@ static int adjacent_edge(const Span<int> corner_verts,
* be used to retrieve the fans from connected_edges.
*/
static void calc_vertex_fans(const int vertex,
const Span<int> new_corner_verts,
const Span<int> corner_verts,
const Span<int> new_corner_edges,
const OffsetIndices<int> polys,
const Span<Vector<int>> edge_to_loop_map,
@ -298,7 +234,7 @@ static void calc_vertex_fans(const int vertex,
/* Add adjacent edges to search stack. */
for (const int loop_i : edge_to_loop_map[curr_edge_i]) {
const int adjacent_edge_i = adjacent_edge(
new_corner_verts, new_corner_edges, loop_i, polys[loop_to_poly_map[loop_i]], vertex);
corner_verts, new_corner_edges, loop_i, polys[loop_to_poly_map[loop_i]], vertex);
/* Find out if this edge was visited already. */
int i = curr_i + 1;
@ -340,18 +276,13 @@ static void calc_vertex_fans(const int vertex,
static void split_edge_per_poly(const int edge_i,
const int new_edge_start,
MutableSpan<Vector<int>> edge_to_loop_map,
MutableSpan<int> corner_edges,
MutableSpan<int2> new_edges,
MutableSpan<int> new_to_old_edges_map)
MutableSpan<int> corner_edges)
{
if (edge_to_loop_map[edge_i].size() <= 1) {
return;
}
int new_edge_index = new_edge_start;
for (const int loop_i : edge_to_loop_map[edge_i].as_span().drop_front(1)) {
const int2 &new_edge(new_edges[edge_i]);
new_edges[new_edge_index] = new_edge;
new_to_old_edges_map[new_edge_index] = edge_i;
edge_to_loop_map[new_edge_index].append({loop_i});
corner_edges[loop_i] = new_edge_index;
new_edge_index++;
@ -401,11 +332,11 @@ void split_edges(Mesh &mesh,
});
const OffsetIndices polys = mesh.polys();
const Array<int> orig_corner_edges = mesh.corner_edges();
IndexMaskMemory memory;
const IndexMask loose_edges = IndexMask::from_bits(mesh.loose_edges().is_loose_bits, memory);
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
Vector<int2> new_edges(new_edges_size);
new_edges.as_mutable_span().take_front(edges.size()).copy_from(edges);
Vector<Vector<int>> edge_to_loop_map(new_edges_size);
threading::parallel_for(edges.index_range(), 512, [&](const IndexRange range) {
@ -414,19 +345,18 @@ void split_edges(Mesh &mesh,
}
});
/* Used for transferring attributes. */
Vector<int> new_to_old_edges_map(new_edges.size());
std::iota(new_to_old_edges_map.begin(), new_to_old_edges_map.end(), 0);
/* Step 1: Split the edges. */
threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
for (const int mask_i : range) {
const int edge_i = mask[mask_i];
split_edge_per_poly(edge_i, edge_offsets[edge_i], edge_to_loop_map, corner_edges);
}
});
/* Step 1: Split the edges. */
mask.foreach_index(GrainSize(512), [&](const int edge_i) {
split_edge_per_poly(edge_i,
edge_offsets[edge_i],
edge_to_loop_map,
corner_edges,
new_edges,
new_to_old_edges_map);
mask.foreach_index([&](const int edge_i) {
split_edge_per_poly(edge_i, edge_offsets[edge_i], edge_to_loop_map, corner_edges);
});
/* Step 1.5: Update topology information (can't parallelize). */
@ -438,6 +368,8 @@ void split_edges(Mesh &mesh,
}
});
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
/* Step 2: Calculate vertex fans. */
Array<Vector<int>> vertex_fan_sizes(mesh.totvert);
threading::parallel_for(IndexRange(mesh.totvert), 512, [&](IndexRange range) {
@ -483,39 +415,43 @@ void split_edges(Mesh &mesh,
vert_to_edge_map[vert],
vertex_fan_sizes[vert],
edge_to_loop_map,
new_edges,
corner_verts,
new_to_old_verts_map);
}
});
/* Step 4: Deduplicate edges. We loop backwards so we can use remove_and_reorder. Although this
* does look bad (3 nested loops), in practice the inner loops are very small. For most meshes,
* there are at most 2 polygons connected to each edge, and hence you'll only get at most 1
* duplicate per edge. */
for (int mask_i = mask.size() - 1; mask_i >= 0; mask_i--) {
const int edge = mask[mask_i];
int start_of_duplicates = edge_offsets[edge];
int end_of_duplicates = start_of_duplicates + num_edge_duplicates[edge] - 1;
for (int duplicate = end_of_duplicates; duplicate >= start_of_duplicates; duplicate--) {
if (naive_edges_equal(new_edges[edge], new_edges[duplicate])) {
merge_edges(
edge, duplicate, corner_edges, edge_to_loop_map, new_edges, new_to_old_edges_map);
break;
}
for (int other = start_of_duplicates; other < duplicate; other++) {
if (naive_edges_equal(new_edges[other], new_edges[duplicate])) {
merge_edges(
other, duplicate, corner_edges, edge_to_loop_map, new_edges, new_to_old_edges_map);
break;
}
}
VectorSet<OrderedEdge> new_edges;
new_edges.reserve(new_edges_size + loose_edges.size());
for (const int i : polys.index_range()) {
const IndexRange poly = polys[i];
for (const int corner : poly) {
const int vert_1 = corner_verts[corner];
const int vert_2 = corner_verts[bke::mesh::poly_corner_next(poly, corner)];
corner_edges[corner] = new_edges.index_of_or_add_as(OrderedEdge(vert_1, vert_2));
}
}
loose_edges.foreach_index([&](const int64_t i) { new_edges.add(OrderedEdge(edges[i])); });
Array<int> new_to_old_edges_map(new_edges.size());
auto index_mask_to_indices = [&](const IndexMask &mask, MutableSpan<int> indices) {
for (const int i : mask.index_range()) {
indices[i] = mask[i];
}
};
index_mask_to_indices(loose_edges,
new_to_old_edges_map.as_mutable_span().take_back(loose_edges.size()));
for (const int i : polys.index_range()) {
const IndexRange poly = polys[i];
for (const int corner : poly) {
const int new_edge_i = corner_edges[corner];
const int old_edge_i = orig_corner_edges[corner];
new_to_old_edges_map[new_edge_i] = old_edge_i;
}
}
/* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */
add_new_vertices(mesh, new_to_old_verts_map);
add_new_edges(mesh, new_edges, new_to_old_edges_map, propagation_info);
add_new_edges(mesh, new_edges.as_span().cast<int2>(), new_to_old_edges_map, propagation_info);
BKE_mesh_tag_edges_split(&mesh);
}

View File

@ -482,6 +482,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_eevee_specular.glsl
shaders/material/gpu_shader_material_emission.glsl
shaders/material/gpu_shader_material_fractal_noise.glsl
shaders/material/gpu_shader_material_fractal_voronoi.glsl
shaders/material/gpu_shader_material_fresnel.glsl
shaders/material/gpu_shader_material_gamma.glsl
shaders/material/gpu_shader_material_geometry.glsl
@ -546,6 +547,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_volume_absorption.glsl
shaders/material/gpu_shader_material_volume_principled.glsl
shaders/material/gpu_shader_material_volume_scatter.glsl
shaders/material/gpu_shader_material_voronoi.glsl
shaders/material/gpu_shader_material_wireframe.glsl
shaders/material/gpu_shader_material_world_normals.glsl

View File

@ -0,0 +1,133 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_material_voronoi.glsl)
#define FRACTAL_VORONOI_X_FX(T) \
VoronoiOutput fractal_voronoi_x_fx(VoronoiParams params, T coord) \
{ \
float amplitude = 1.0; \
float max_amplitude = 0.0; \
float scale = 1.0; \
\
VoronoiOutput Output; \
Output.Distance = 0.0; \
Output.Color = vec3(0.0, 0.0, 0.0); \
Output.Position = vec4(0.0, 0.0, 0.0, 0.0); \
bool zero_input = params.detail == 0.0 || params.roughness == 0.0 || \
params.lacunarity == 0.0; \
\
for (int i = 0; i <= ceil(params.detail); ++i) { \
VoronoiOutput octave; \
if (params.feature == 0) /* SHD_VORONOI_F1 */ { \
octave = voronoi_f1(params, coord * scale); \
} \
else if (params.feature == 2) /* SHD_VORONOI_SMOOTH_F1 */ { \
octave = voronoi_smooth_f1(params, coord * scale); \
} \
else { \
octave = voronoi_f2(params, coord * scale); \
} \
\
if (zero_input) { \
max_amplitude = 1.0; \
Output = octave; \
break; \
} \
else if (i <= params.detail) { \
max_amplitude += amplitude; \
Output.Distance += octave.Distance * amplitude; \
Output.Color += octave.Color * amplitude; \
Output.Position = mix(Output.Position, octave.Position / scale, amplitude); \
scale *= params.lacunarity; \
amplitude *= params.roughness; \
} \
else { \
float remainder = params.detail - floor(params.detail); \
if (remainder != 0.0) { \
max_amplitude = mix(max_amplitude, max_amplitude + amplitude, remainder); \
Output.Distance = mix( \
Output.Distance, Output.Distance + octave.Distance * amplitude, remainder); \
Output.Color = mix(Output.Color, Output.Color + octave.Color * amplitude, remainder); \
Output.Position = mix(Output.Position, \
mix(Output.Position, octave.Position / scale, amplitude), \
remainder); \
} \
} \
} \
\
if (params.normalize) { \
Output.Distance /= max_amplitude * params.max_distance; \
Output.Color /= max_amplitude; \
} \
\
Output.Position = safe_divide(Output.Position, params.scale); \
\
return Output; \
}
#define FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(T) \
float fractal_voronoi_distance_to_edge(VoronoiParams params, T coord) \
{ \
float amplitude = 1.0; \
float max_amplitude = 0.5 + 0.5 * params.randomness; \
float scale = 1.0; \
float distance = 8.0; \
\
bool zero_input = params.detail == 0.0 || params.roughness == 0.0 || \
params.lacunarity == 0.0; \
\
for (int i = 0; i <= ceil(params.detail); ++i) { \
float octave_distance = voronoi_distance_to_edge(params, coord * scale); \
\
if (zero_input) { \
distance = octave_distance; \
break; \
} \
else if (i <= params.detail) { \
max_amplitude = mix(max_amplitude, (0.5 + 0.5 * params.randomness) / scale, amplitude); \
distance = mix(distance, min(distance, octave_distance / scale), amplitude); \
scale *= params.lacunarity; \
amplitude *= params.roughness; \
} \
else { \
float remainder = params.detail - floor(params.detail); \
if (remainder != 0.0) { \
float lerp_amplitude = mix( \
max_amplitude, (0.5 + 0.5 * params.randomness) / scale, amplitude); \
max_amplitude = mix(max_amplitude, lerp_amplitude, remainder); \
float lerp_distance = mix(distance, min(distance, octave_distance / scale), amplitude); \
distance = mix(distance, min(distance, lerp_distance), remainder); \
} \
} \
} \
\
if (params.normalize) { \
distance /= max_amplitude; \
} \
\
return distance; \
}
/* **** 1D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(float)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(float)
/* **** 2D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(vec2)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(vec2)
/* **** 3D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(vec3)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(vec3)
/* **** 4D Fractal Voronoi **** */
FRACTAL_VORONOI_X_FX(vec4)
FRACTAL_VORONOI_DISTANCE_TO_EDGE_FUNCTION(vec4)

View File

@ -0,0 +1,889 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
/*
* Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez.
*
* Smooth Voronoi:
*
* - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
*
* Distance To Edge based on:
*
* - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
* - https://www.shadertoy.com/view/ldl3W8
*
* With optimization to change -2..2 scan window to -1..1 for better performance,
* as explained in https://www.shadertoy.com/view/llG3zy.
*/
struct VoronoiParams {
float scale;
float detail;
float roughness;
float lacunarity;
float smoothness;
float exponent;
float randomness;
float max_distance;
bool normalize;
int feature;
int metric;
};
struct VoronoiOutput {
float Distance;
vec3 Color;
vec4 Position;
};
/* **** Distance Functions **** */
float voronoi_distance(float a, float b)
{
return abs(a - b);
}
float voronoi_distance(vec2 a, vec2 b, VoronoiParams params)
{
if (params.metric == 0) // SHD_VORONOI_EUCLIDEAN
{
return distance(a, b);
}
else if (params.metric == 1) // SHD_VORONOI_MANHATTAN
{
return abs(a.x - b.x) + abs(a.y - b.y);
}
else if (params.metric == 2) // SHD_VORONOI_CHEBYCHEV
{
return max(abs(a.x - b.x), abs(a.y - b.y));
}
else if (params.metric == 3) // SHD_VORONOI_MINKOWSKI
{
return pow(pow(abs(a.x - b.x), params.exponent) + pow(abs(a.y - b.y), params.exponent),
1.0 / params.exponent);
}
else {
return 0.0;
}
}
float voronoi_distance(vec3 a, vec3 b, VoronoiParams params)
{
if (params.metric == 0) // SHD_VORONOI_EUCLIDEAN
{
return distance(a, b);
}
else if (params.metric == 1) // SHD_VORONOI_MANHATTAN
{
return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z);
}
else if (params.metric == 2) // SHD_VORONOI_CHEBYCHEV
{
return max(abs(a.x - b.x), max(abs(a.y - b.y), abs(a.z - b.z)));
}
else if (params.metric == 3) // SHD_VORONOI_MINKOWSKI
{
return pow(pow(abs(a.x - b.x), params.exponent) + pow(abs(a.y - b.y), params.exponent) +
pow(abs(a.z - b.z), params.exponent),
1.0 / params.exponent);
}
else {
return 0.0;
}
}
float voronoi_distance(vec4 a, vec4 b, VoronoiParams params)
{
if (params.metric == 0) // SHD_VORONOI_EUCLIDEAN
{
return distance(a, b);
}
else if (params.metric == 1) // SHD_VORONOI_MANHATTAN
{
return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) + abs(a.w - b.w);
}
else if (params.metric == 2) // SHD_VORONOI_CHEBYCHEV
{
return max(abs(a.x - b.x), max(abs(a.y - b.y), max(abs(a.z - b.z), abs(a.w - b.w))));
}
else if (params.metric == 3) // SHD_VORONOI_MINKOWSKI
{
return pow(pow(abs(a.x - b.x), params.exponent) + pow(abs(a.y - b.y), params.exponent) +
pow(abs(a.z - b.z), params.exponent) + pow(abs(a.w - b.w), params.exponent),
1.0 / params.exponent);
}
else {
return 0.0;
}
}
/* **** 1D Voronoi **** */
vec4 voronoi_position(float coord)
{
return vec4(0.0, 0.0, 0.0, coord);
}
VoronoiOutput voronoi_f1(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float minDistance = 8.0;
float targetOffset = 0.0;
float targetPosition = 0.0;
for (int i = -1; i <= 1; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_float_to_vec3(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float smoothDistance = 8.0;
float smoothPosition = 0.0;
vec3 smoothColor = vec3(0.0);
for (int i = -2; i <= 2; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
vec3 cellColor = hash_float_to_vec3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
float offsetF1 = 0.0;
float positionF1 = 0.0;
float offsetF2 = 0.0;
float positionF2 = 0.0;
for (int i = -1; i <= 1; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_float_to_vec3(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float midPointPosition = hash_float_to_float(cellPosition) * params.randomness;
float leftPointPosition = -1.0 + hash_float_to_float(cellPosition - 1.0) * params.randomness;
float rightPointPosition = 1.0 + hash_float_to_float(cellPosition + 1.0) * params.randomness;
float distanceToMidLeft = abs((midPointPosition + leftPointPosition) / 2.0 - localPosition);
float distanceToMidRight = abs((midPointPosition + rightPointPosition) / 2.0 - localPosition);
return min(distanceToMidLeft, distanceToMidRight);
}
float voronoi_n_sphere_radius(VoronoiParams params, float coord)
{
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float closestPoint = 0.0;
float closestPointOffset = 0.0;
float minDistance = 8.0;
for (int i = -1; i <= 1; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = abs(pointPosition - localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
minDistance = 8.0;
float closestPointToClosestPoint = 0.0;
for (int i = -1; i <= 1; i++) {
if (i == 0) {
continue;
}
float cellOffset = i + closestPointOffset;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = abs(closestPoint - pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
return abs(closestPointToClosestPoint - closestPoint) / 2.0;
}
/* **** 2D Voronoi **** */
vec4 voronoi_position(vec2 coord)
{
return vec4(coord.x, coord.y, 0.0, 0.0);
}
VoronoiOutput voronoi_f1(VoronoiParams params, vec2 coord)
{
vec2 cellPosition = floor(coord);
vec2 localPosition = coord - cellPosition;
float minDistance = 8.0;
vec2 targetOffset = vec2(0.0);
vec2 targetPosition = vec2(0.0);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec2 cellOffset = vec2(i, j);
vec2 pointPosition = cellOffset +
hash_vec2_to_vec2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_vec2_to_vec3(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, vec2 coord)
{
vec2 cellPosition = floor(coord);
vec2 localPosition = coord - cellPosition;
float smoothDistance = 8.0;
vec3 smoothColor = vec3(0.0);
vec2 smoothPosition = vec2(0.0);
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
vec2 cellOffset = vec2(i, j);
vec2 pointPosition = cellOffset +
hash_vec2_to_vec2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
vec3 cellColor = hash_vec2_to_vec3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, vec2 coord)
{
vec2 cellPosition = floor(coord);
vec2 localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
vec2 offsetF1 = vec2(0.0);
vec2 positionF1 = vec2(0.0);
vec2 offsetF2 = vec2(0.0);
vec2 positionF2 = vec2(0.0);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec2 cellOffset = vec2(i, j);
vec2 pointPosition = cellOffset +
hash_vec2_to_vec2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_vec2_to_vec3(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, vec2 coord)
{
vec2 cellPosition = floor(coord);
vec2 localPosition = coord - cellPosition;
vec2 vectorToClosest = vec2(0.0);
float minDistance = 8.0;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec2 cellOffset = vec2(i, j);
vec2 vectorToPoint = cellOffset +
hash_vec2_to_vec2(cellPosition + cellOffset) * params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
minDistance = 8.0;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec2 cellOffset = vec2(i, j);
vec2 vectorToPoint = cellOffset +
hash_vec2_to_vec2(cellPosition + cellOffset) * params.randomness -
localPosition;
vec2 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
normalize(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
return minDistance;
}
float voronoi_n_sphere_radius(VoronoiParams params, vec2 coord)
{
vec2 cellPosition = floor(coord);
vec2 localPosition = coord - cellPosition;
vec2 closestPoint = vec2(0.0);
vec2 closestPointOffset = vec2(0.0);
float minDistance = 8.0;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec2 cellOffset = vec2(i, j);
vec2 pointPosition = cellOffset +
hash_vec2_to_vec2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
minDistance = 8.0;
vec2 closestPointToClosestPoint = vec2(0.0);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0) {
continue;
}
vec2 cellOffset = vec2(i, j) + closestPointOffset;
vec2 pointPosition = cellOffset +
hash_vec2_to_vec2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0;
}
/* **** 3D Voronoi **** */
vec4 voronoi_position(vec3 coord)
{
return vec4(coord.x, coord.y, coord.z, 0.0);
}
VoronoiOutput voronoi_f1(VoronoiParams params, vec3 coord)
{
vec3 cellPosition = floor(coord);
vec3 localPosition = coord - cellPosition;
float minDistance = 8.0;
vec3 targetOffset = vec3(0.0);
vec3 targetPosition = vec3(0.0);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec3 cellOffset = vec3(i, j, k);
vec3 pointPosition = cellOffset +
hash_vec3_to_vec3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_vec3_to_vec3(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, vec3 coord)
{
vec3 cellPosition = floor(coord);
vec3 localPosition = coord - cellPosition;
float smoothDistance = 8.0;
vec3 smoothColor = vec3(0.0);
vec3 smoothPosition = vec3(0.0);
for (int k = -2; k <= 2; k++) {
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
vec3 cellOffset = vec3(i, j, k);
vec3 pointPosition = cellOffset +
hash_vec3_to_vec3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
vec3 cellColor = hash_vec3_to_vec3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, vec3 coord)
{
vec3 cellPosition = floor(coord);
vec3 localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
vec3 offsetF1 = vec3(0.0);
vec3 positionF1 = vec3(0.0);
vec3 offsetF2 = vec3(0.0);
vec3 positionF2 = vec3(0.0);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec3 cellOffset = vec3(i, j, k);
vec3 pointPosition = cellOffset +
hash_vec3_to_vec3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_vec3_to_vec3(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, vec3 coord)
{
vec3 cellPosition = floor(coord);
vec3 localPosition = coord - cellPosition;
vec3 vectorToClosest = vec3(0.0);
float minDistance = 8.0;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec3 cellOffset = vec3(i, j, k);
vec3 vectorToPoint = cellOffset +
hash_vec3_to_vec3(cellPosition + cellOffset) * params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
minDistance = 8.0;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec3 cellOffset = vec3(i, j, k);
vec3 vectorToPoint = cellOffset +
hash_vec3_to_vec3(cellPosition + cellOffset) * params.randomness -
localPosition;
vec3 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
normalize(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
}
return minDistance;
}
float voronoi_n_sphere_radius(VoronoiParams params, vec3 coord)
{
vec3 cellPosition = floor(coord);
vec3 localPosition = coord - cellPosition;
vec3 closestPoint = vec3(0.0);
vec3 closestPointOffset = vec3(0.0);
float minDistance = 8.0;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec3 cellOffset = vec3(i, j, k);
vec3 pointPosition = cellOffset +
hash_vec3_to_vec3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
minDistance = 8.0;
vec3 closestPointToClosestPoint = vec3(0.0);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0 && k == 0) {
continue;
}
vec3 cellOffset = vec3(i, j, k) + closestPointOffset;
vec3 pointPosition = cellOffset +
hash_vec3_to_vec3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0;
}
/* **** 4D Voronoi **** */
vec4 voronoi_position(vec4 coord)
{
return coord;
}
VoronoiOutput voronoi_f1(VoronoiParams params, vec4 coord)
{
vec4 cellPosition = floor(coord);
vec4 localPosition = coord - cellPosition;
float minDistance = 8.0;
vec4 targetOffset = vec4(0.0);
vec4 targetPosition = vec4(0.0);
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec4 cellOffset = vec4(i, j, k, u);
vec4 pointPosition = cellOffset +
hash_vec4_to_vec4(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
}
VoronoiOutput octave;
octave.Distance = minDistance;
octave.Color = hash_vec4_to_vec3(cellPosition + targetOffset);
octave.Position = voronoi_position(targetPosition + cellPosition);
return octave;
}
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, vec4 coord)
{
vec4 cellPosition = floor(coord);
vec4 localPosition = coord - cellPosition;
float smoothDistance = 8.0;
vec3 smoothColor = vec3(0.0);
vec4 smoothPosition = vec4(0.0);
for (int u = -2; u <= 2; u++) {
for (int k = -2; k <= 2; k++) {
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
vec4 cellOffset = vec4(i, j, k, u);
vec4 pointPosition = cellOffset +
hash_vec4_to_vec4(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float h = smoothstep(
0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0 - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0 + 3.0 * params.smoothness;
vec3 cellColor = hash_vec4_to_vec3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
}
}
VoronoiOutput octave;
octave.Distance = smoothDistance;
octave.Color = smoothColor;
octave.Position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
VoronoiOutput voronoi_f2(VoronoiParams params, vec4 coord)
{
vec4 cellPosition = floor(coord);
vec4 localPosition = coord - cellPosition;
float distanceF1 = 8.0;
float distanceF2 = 8.0;
vec4 offsetF1 = vec4(0.0);
vec4 positionF1 = vec4(0.0);
vec4 offsetF2 = vec4(0.0);
vec4 positionF2 = vec4(0.0);
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec4 cellOffset = vec4(i, j, k, u);
vec4 pointPosition = cellOffset +
hash_vec4_to_vec4(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
}
}
}
VoronoiOutput octave;
octave.Distance = distanceF2;
octave.Color = hash_vec4_to_vec3(cellPosition + offsetF2);
octave.Position = voronoi_position(positionF2 + cellPosition);
return octave;
}
float voronoi_distance_to_edge(VoronoiParams params, vec4 coord)
{
vec4 cellPosition = floor(coord);
vec4 localPosition = coord - cellPosition;
vec4 vectorToClosest = vec4(0.0);
float minDistance = 8.0;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec4 cellOffset = vec4(i, j, k, u);
vec4 vectorToPoint = cellOffset +
hash_vec4_to_vec4(cellPosition + cellOffset) * params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
}
minDistance = 8.0;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec4 cellOffset = vec4(i, j, k, u);
vec4 vectorToPoint = cellOffset +
hash_vec4_to_vec4(cellPosition + cellOffset) * params.randomness -
localPosition;
vec4 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
normalize(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
}
}
return minDistance;
}
float voronoi_n_sphere_radius(VoronoiParams params, vec4 coord)
{
vec4 cellPosition = floor(coord);
vec4 localPosition = coord - cellPosition;
vec4 closestPoint = vec4(0.0);
vec4 closestPointOffset = vec4(0.0);
float minDistance = 8.0;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec4 cellOffset = vec4(i, j, k, u);
vec4 pointPosition = cellOffset +
hash_vec4_to_vec4(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
}
minDistance = 8.0;
vec4 closestPointToClosestPoint = vec4(0.0);
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0 && k == 0 && u == 0) {
continue;
}
vec4 cellOffset = vec4(i, j, k, u) + closestPointOffset;
vec4 pointPosition = cellOffset +
hash_vec4_to_vec4(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0;
}

View File

@ -218,13 +218,29 @@ static bool uri_from_filename(const char *path, char *uri)
char orig_uri[URI_MAX];
#ifdef WIN32
if (strlen(path) < 2 && path[1] != ':') {
/* Not a correct absolute path. */
return 0;
bool path_is_unc = BLI_path_is_unc(path);
char path_unc_normalized[FILE_MAX];
if (path_is_unc) {
STRNCPY(path_unc_normalized, path);
BLI_path_normalize_unc(path_unc_normalized, sizeof(path_unc_normalized));
path = path_unc_normalized;
/* Assign again because a normalized UNC path may resolve to a drive letter. */
path_is_unc = BLI_path_is_unc(path);
}
if (path_is_unc) {
/* Skip over the `\\` prefix, it's not needed for a URI. */
SNPRINTF(orig_uri, "file://%s", BLI_path_slash_skip(path));
}
else if (BLI_path_is_win32_drive(path)) {
SNPRINTF(orig_uri, "file:///%s", path);
/* Always use an uppercase drive/volume letter in the URI. */
orig_uri[8] = char(toupper(orig_uri[8]));
}
else {
/* Not a correct absolute path with a drive letter or UNC prefix. */
return false;
}
SNPRINTF(orig_uri, "file:///%s", path);
/* Always use an uppercase drive/volume letter in the URI. */
orig_uri[8] = char(toupper(orig_uri[8]));
BLI_str_replace_char(orig_uri, '\\', '/');
#else
SNPRINTF(orig_uri, "file://%s", path);

View File

@ -42,11 +42,11 @@ void FileBuffer::close_file()
void FileBuffer::write_header_element(StringRef name, int count)
{
write_fstring("element {} {}\n", (std::string_view)name, count);
write_fstring("element {} {}\n", std::string_view(name), count);
}
void FileBuffer::write_header_scalar_property(StringRef dataType, StringRef name)
{
write_fstring("property {} {}\n", (std::string_view)dataType, (std::string_view)name);
write_fstring("property {} {}\n", std::string_view(dataType), std::string_view(name));
}
void FileBuffer::write_header_list_property(StringRef countType,
@ -54,14 +54,14 @@ void FileBuffer::write_header_list_property(StringRef countType,
StringRef name)
{
write_fstring("property list {} {} {}\n",
(std::string_view)countType,
(std::string_view)dataType,
(std::string_view)name);
std::string_view(countType),
std::string_view(dataType),
std::string_view(name));
}
void FileBuffer::write_string(StringRef s)
{
write_fstring("{}\n", (std::string_view)s);
write_fstring("{}\n", std::string_view(s));
}
void FileBuffer::write_newline()

View File

@ -109,11 +109,11 @@ class FormatHandler : NonCopyable, NonMovable {
}
void write_obj_usemtl(StringRef s)
{
write_impl("usemtl {}\n", (std::string_view)s);
write_impl("usemtl {}\n", std::string_view(s));
}
void write_obj_mtllib(StringRef s)
{
write_impl("mtllib {}\n", (std::string_view)s);
write_impl("mtllib {}\n", std::string_view(s));
}
void write_obj_smooth(int s)
{
@ -121,11 +121,11 @@ class FormatHandler : NonCopyable, NonMovable {
}
void write_obj_group(StringRef s)
{
write_impl("g {}\n", (std::string_view)s);
write_impl("g {}\n", std::string_view(s));
}
void write_obj_object(StringRef s)
{
write_impl("o {}\n", (std::string_view)s);
write_impl("o {}\n", std::string_view(s));
}
void write_obj_edge(int a, int b)
{
@ -170,7 +170,7 @@ class FormatHandler : NonCopyable, NonMovable {
void write_mtl_newmtl(StringRef s)
{
write_impl("newmtl {}\n", (std::string_view)s);
write_impl("newmtl {}\n", std::string_view(s));
}
void write_mtl_float(const char *type, float v)
{
@ -187,12 +187,12 @@ class FormatHandler : NonCopyable, NonMovable {
/* NOTE: options, if present, will have its own leading space. */
void write_mtl_map(const char *type, StringRef options, StringRef value)
{
write_impl("{}{} {}\n", type, (std::string_view)options, (std::string_view)value);
write_impl("{}{} {}\n", type, std::string_view(options), std::string_view(value));
}
void write_string(StringRef s)
{
write_impl("{}\n", (std::string_view)s);
write_impl("{}\n", std::string_view(s));
}
private:

View File

@ -1108,7 +1108,9 @@ typedef struct NodeTexVoronoi {
int dimensions;
int feature;
int distance;
int normalize;
int coloring DNA_DEPRECATED;
char _pad[4];
} NodeTexVoronoi;
typedef struct NodeTexMusgrave {

View File

@ -5922,6 +5922,11 @@ static void def_sh_tex_voronoi(StructRNA *srna)
RNA_def_property_ui_text(
prop, "Feature Output", "The Voronoi feature that the node will compute");
RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "normalize", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "normalize", 0);
RNA_def_property_ui_text(prop, "Normalize", "Normalize output Distance to 0.0 to 1.0 range");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_sh_tex_wave(StructRNA *srna)

View File

@ -1258,7 +1258,10 @@ static void internal_dependencies_panel_draw(const bContext * /*C*/, Panel *pane
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data);
uiItemR(layout, ptr, "simulation_bake_directory", 0, "Bake", ICON_NONE);
uiLayout *col = uiLayoutColumn(layout, false);
uiLayoutSetPropSep(col, true);
uiLayoutSetPropDecorate(col, false);
uiItemR(col, ptr, "simulation_bake_directory", 0, "Bake", ICON_NONE);
geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
if (tree_log == nullptr) {

View File

@ -825,10 +825,19 @@ class RenderLayerOperation : public NodeOperation {
void execute() override
{
const int view_layer = bnode().custom1;
GPUTexture *combined_texture = context().get_input_texture(view_layer, RE_PASSNAME_COMBINED);
execute_pass("Image", combined_texture, "compositor_read_pass_color");
execute_pass("Alpha", combined_texture, "compositor_read_pass_alpha");
Result &image_result = get_result("Image");
Result &alpha_result = get_result("Alpha");
if (image_result.should_compute() || alpha_result.should_compute()) {
GPUTexture *combined_texture = context().get_input_texture(view_layer, RE_PASSNAME_COMBINED);
if (image_result.should_compute()) {
execute_pass(image_result, combined_texture, "compositor_read_pass_color");
}
if (alpha_result.should_compute()) {
execute_pass(alpha_result, combined_texture, "compositor_read_pass_alpha");
}
}
/* Other output passes are not supported for now, so allocate them as invalid. */
for (const bNodeSocket *output : this->node()->output_sockets()) {
@ -836,15 +845,20 @@ class RenderLayerOperation : public NodeOperation {
continue;
}
Result &result = get_result(output->identifier);
if (!result.should_compute()) {
continue;
}
GPUTexture *pass_texture = context().get_input_texture(view_layer, output->identifier);
if (output->type == SOCK_FLOAT) {
execute_pass(output->identifier, pass_texture, "compositor_read_pass_float");
execute_pass(result, pass_texture, "compositor_read_pass_float");
}
else if (output->type == SOCK_VECTOR) {
execute_pass(output->identifier, pass_texture, "compositor_read_pass_vector");
execute_pass(result, pass_texture, "compositor_read_pass_vector");
}
else if (output->type == SOCK_RGBA) {
execute_pass(output->identifier, pass_texture, "compositor_read_pass_color");
execute_pass(result, pass_texture, "compositor_read_pass_color");
}
else {
BLI_assert_unreachable();
@ -852,15 +866,11 @@ class RenderLayerOperation : public NodeOperation {
}
}
void execute_pass(const char *pass_name, GPUTexture *pass_texture, const char *shader_name)
void execute_pass(Result &result, GPUTexture *pass_texture, const char *shader_name)
{
Result &image_result = get_result(pass_name);
if (!image_result.should_compute()) {
return;
}
if (pass_texture == nullptr) {
/* Pass not rendered yet, or not supported by viewport. */
image_result.allocate_invalid();
result.allocate_invalid();
context().set_info_message("Viewport compositor setup not fully supported");
return;
}
@ -878,14 +888,14 @@ class RenderLayerOperation : public NodeOperation {
GPU_texture_bind(pass_texture, input_unit);
const int2 compositing_region_size = context().get_compositing_region_size();
image_result.allocate_texture(Domain(compositing_region_size));
image_result.bind_as_image(shader, "output_img");
result.allocate_texture(Domain(compositing_region_size));
result.bind_as_image(shader, "output_img");
compute_dispatch_threads_at_least(shader, compositing_region_size);
GPU_shader_unbind();
GPU_texture_unbind(pass_texture);
image_result.unbind_as_image();
result.unbind_as_image();
}
};

View File

@ -20,6 +20,7 @@ set(INC
../../makesrna
../../render
../../windowmanager
../../../../extern/fmtlib/include
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@ -204,6 +205,7 @@ set(LIB
bf_functions
bf_geometry
bf_nodes
extern_fmtlib
)
if(WITH_BULLET)

View File

@ -29,6 +29,8 @@
#include "node_geometry_util.hh"
#include <fmt/format.h>
namespace blender::nodes::node_geo_deform_curves_on_surface_cc {
using bke::CurvesGeometry;
@ -274,18 +276,16 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!mesh_attributes_eval.contains(uv_map_name)) {
pass_through_input();
char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: \"%s\""),
uv_map_name.c_str());
const std::string message = fmt::format(TIP_("Evaluated surface missing UV map: \"{}\""),
std::string_view(uv_map_name));
params.error_message_add(NodeWarningType::Error, message);
MEM_freeN(message);
return;
}
if (!mesh_attributes_orig.contains(uv_map_name)) {
pass_through_input();
char *message = BLI_sprintfN(TIP_("Original surface missing UV map: \"%s\""),
uv_map_name.c_str());
const std::string message = fmt::format(TIP_("Original surface missing UV map: \"{}\""),
std::string_view(uv_map_name));
params.error_message_add(NodeWarningType::Error, message);
MEM_freeN(message);
return;
}
if (!mesh_attributes_eval.contains(rest_position_name)) {
@ -398,10 +398,9 @@ static void node_geo_exec(GeoNodeExecParams params)
curves.tag_positions_changed();
if (invalid_uv_count) {
char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves"),
invalid_uv_count.load());
const std::string message = fmt::format(TIP_("Invalid surface UVs on {} curves"),
invalid_uv_count.load());
params.error_message_add(NodeWarningType::Warning, message);
MEM_freeN(message);
}
params.set_output("Curves", curves_geometry);

View File

@ -6,6 +6,8 @@
#include "NOD_socket_search_link.hh"
#include <fmt/format.h>
namespace blender::nodes::node_geo_remove_attribute_cc {
static void node_declare(NodeDeclarationBuilder &b)
@ -62,15 +64,13 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (!attribute_exists) {
char *message = BLI_sprintfN(TIP_("Attribute does not exist: \"%s\""), name.c_str());
const std::string message = fmt::format(TIP_("Attribute does not exist: \"{}\""), name);
params.error_message_add(NodeWarningType::Warning, message);
MEM_freeN(message);
}
if (cannot_delete) {
char *message = BLI_sprintfN(TIP_("Cannot delete built-in attribute with name \"%s\""),
name.c_str());
const std::string message = fmt::format(TIP_("Cannot delete built-in attribute: \"{}\""),
name);
params.error_message_add(NodeWarningType::Warning, message);
MEM_freeN(message);
}
params.set_output("Geometry", std::move(geometry_set));

View File

@ -15,6 +15,8 @@
#include "node_geometry_util.hh"
#include <fmt/format.h>
namespace blender::nodes::node_geo_store_named_attribute_cc {
NODE_STORAGE_FUNCS(NodeGeometryStoreNamedAttribute)
@ -94,7 +96,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
std::string name = params.extract_input<std::string>("Name");
const std::string name = params.extract_input<std::string>("Name");
if (name.empty()) {
params.set_output("Geometry", std::move(geometry_set));
@ -182,13 +184,12 @@ static void node_geo_exec(GeoNodeExecParams params)
RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &domain_name);
const char *type_name = nullptr;
RNA_enum_name_from_value(rna_enum_attribute_type_items, data_type, &type_name);
char *message = BLI_sprintfN(
TIP_("Failed to write to attribute \"%s\" with domain \"%s\" and type \"%s\""),
name.c_str(),
const std::string message = fmt::format(
TIP_("Failed to write to attribute \"{}\" with domain \"{}\" and type \"{}\""),
name,
TIP_(domain_name),
TIP_(type_name));
params.error_message_add(NodeWarningType::Warning, message);
MEM_freeN(message);
}
params.set_output("Geometry", std::move(geometry_set));

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
#include "BLI_implicit_sharing.h"
struct GPUTexture;
struct ImBuf;
struct Image;
struct ImageFormatData;
@ -44,16 +45,20 @@ typedef struct Render Render;
/* Buffer of a floating point values which uses implicit sharing.
*
* The buffer is allocated by render passes creation, and then is shared with the render result
* and image buffer. */
* and image buffer.
*
* The GPU texture is an optional read-only copy of the render buffer in GPU memory. */
typedef struct RenderBuffer {
float *data;
const ImplicitSharingInfoHandle *sharing_info;
struct GPUTexture *gpu_texture;
} RenderBuffer;
/* Specialized render buffer to store 8bpp passes. */
typedef struct RenderByteBuffer {
uint8_t *data;
const ImplicitSharingInfoHandle *sharing_info;
struct GPUTexture *gpu_texture;
} RenderByteBuffer;
/* Render Result usage:
@ -206,6 +211,7 @@ void RE_FreeAllRender(void);
* On file load, free render results.
*/
void RE_FreeAllRenderResults(void);
/**
* On file load or changes engines, free persistent render data.
* Assumes no engines are currently rendering.
@ -216,6 +222,12 @@ void RE_FreeAllPersistentData(void);
*/
void RE_FreePersistentData(const struct Scene *scene);
/*
* Free cached GPU textures to reduce memory usage. Before rendering all are cleard
* and on UI changes when detected they are no longer used.
*/
void RE_FreeGPUTextureCaches(const bool only_unused);
/**
* Get results and statistics.
*/
@ -467,6 +479,11 @@ struct RenderPass *RE_pass_find_by_type(struct RenderLayer *rl,
*/
void RE_pass_set_buffer_data(struct RenderPass *pass, float *data);
/**
* Ensure a GPU texture corresponding to the render buffer data exists.
*/
struct GPUTexture *RE_pass_ensure_gpu_texture_cache(struct Render *re, struct RenderPass *rpass);
/* shaded view or baking options */
#define RE_BAKE_NORMALS 0
#define RE_BAKE_DISPLACEMENT 1

View File

@ -73,6 +73,9 @@ class Context : public realtime_compositor::Context {
/* Viewer output texture. */
GPUTexture *viewer_output_texture_ = nullptr;
/* Texture pool. */
TexturePool &render_texture_pool_;
public:
Context(const Scene &scene,
const RenderData &render_data,
@ -85,7 +88,8 @@ class Context : public realtime_compositor::Context {
render_data_(render_data),
node_tree_(node_tree),
use_file_output_(use_file_output),
view_name_(view_name)
view_name_(view_name),
render_texture_pool_(texture_pool)
{
}
@ -175,9 +179,6 @@ class Context : public realtime_compositor::Context {
GPUTexture *get_input_texture(int view_layer_id, const char *pass_name) override
{
/* TODO: eventually this should get cached on the RenderResult itself when
* it supports storing GPU buffers, for faster updates. But will also need
* some eviction strategy to avoid too much GPU memory usage. */
Render *re = RE_GetSceneRender(&scene_);
RenderResult *rr = nullptr;
GPUTexture *input_texture = nullptr;
@ -195,34 +196,12 @@ class Context : public realtime_compositor::Context {
&rl->passes, pass_name, offsetof(RenderPass, name));
if (rpass && rpass->buffer.data) {
const int2 size(rl->rectx, rl->recty);
input_texture = RE_pass_ensure_gpu_texture_cache(re, rpass);
if (rpass->channels == 1) {
input_texture = texture_pool().acquire_float(size);
if (input_texture) {
GPU_texture_update(input_texture, GPU_DATA_FLOAT, rpass->buffer.data);
}
}
else if (rpass->channels == 3) {
input_texture = texture_pool().acquire_color(size);
if (input_texture) {
/* TODO: conversion could be done as part of GPU upload somehow? */
const float *rgb_buffer = rpass->buffer.data;
Vector<float> rgba_buffer(4 * size.x * size.y);
for (size_t i = 0; i < size_t(size.x) * size_t(size.y); i++) {
rgba_buffer[i * 4 + 0] = rgb_buffer[i * 3 + 0];
rgba_buffer[i * 4 + 1] = rgb_buffer[i * 3 + 1];
rgba_buffer[i * 4 + 2] = rgb_buffer[i * 3 + 2];
rgba_buffer[i * 4 + 3] = 1.0f;
}
GPU_texture_update(input_texture, GPU_DATA_FLOAT, rgba_buffer.data());
}
}
else if (rpass->channels == 4) {
input_texture = texture_pool().acquire_color(size);
if (input_texture) {
GPU_texture_update(input_texture, GPU_DATA_FLOAT, rpass->buffer.data);
}
if (input_texture) {
/* Don't assume render keeps texture around, add our own reference. */
GPU_texture_ref(input_texture);
render_texture_pool_.textures_.append(input_texture);
}
}
}

View File

@ -23,6 +23,7 @@
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "DNA_windowmanager_types.h"
#include "MEM_guardedalloc.h"
@ -331,6 +332,7 @@ void RE_ClearResult(Render *re)
if (re) {
render_result_free(re->result);
re->result = nullptr;
re->result_has_gpu_texture_caches = false;
}
}
@ -647,6 +649,7 @@ void RE_FreeAllRenderResults(void)
re->result = nullptr;
re->pushedresult = nullptr;
re->result_has_gpu_texture_caches = false;
}
}
@ -661,9 +664,58 @@ void RE_FreeAllPersistentData(void)
}
}
void RE_FreeGPUTextureCaches(const bool only_unused)
{
LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
if (!re->result_has_gpu_texture_caches) {
continue;
}
Scene *scene = re->scene;
bool do_free = true;
/* Detect if scene is using realtime compositing, and if either a node editor is
* showing the nodes, or an image editor is showing the render result or viewer. */
if (only_unused && scene && scene->use_nodes && scene->nodetree &&
scene->nodetree->execution_mode == NTREE_EXECUTION_MODE_REALTIME)
{
wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
LISTBASE_FOREACH (const wmWindow *, win, &wm->windows) {
const bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
const SpaceLink &space = *static_cast<const SpaceLink *>(area->spacedata.first);
if (space.spacetype == SPACE_NODE) {
const SpaceNode &snode = reinterpret_cast<const SpaceNode &>(space);
if (snode.nodetree == scene->nodetree) {
do_free = false;
}
}
else if (space.spacetype == SPACE_IMAGE) {
const SpaceImage &sima = reinterpret_cast<const SpaceImage &>(space);
if (sima.image && sima.image->source == IMA_SRC_VIEWER) {
do_free = false;
}
}
}
}
}
if (do_free) {
RenderResult *result = RE_AcquireResultWrite(re);
if (result != nullptr) {
render_result_free_gpu_texture_caches(result);
}
re->result_has_gpu_texture_caches = false;
RE_ReleaseResult(re);
}
}
}
static void re_free_persistent_data(Render *re)
{
/* If engine is currently rendering, just wait for it to be freed when it finishes rendering. */
/* If engine is currently rendering, just wait for it to be freed when it finishes rendering.
*/
if (re->engine && !(re->engine->flag & RE_ENGINE_RENDERING)) {
RE_engine_free(re->engine);
re->engine = nullptr;
@ -1765,8 +1817,8 @@ static void render_pipeline_free(Render *re)
/* Destroy the opengl context in the correct thread. */
RE_system_gpu_context_destroy(re);
/* In the case the engine did not mark tiles as finished (un-highlight, which could happen in the
* case of cancelled render) ensure the storage is empty. */
/* In the case the engine did not mark tiles as finished (un-highlight, which could happen in
* the case of cancelled render) ensure the storage is empty. */
if (re->highlighted_tiles != nullptr) {
BLI_mutex_lock(&re->highlighted_tiles_mutex);
@ -1808,6 +1860,9 @@ void RE_RenderFrame(Render *re,
render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_PRE);
/* Reduce GPU memory usage so renderer has more space. */
RE_FreeGPUTextureCaches(false);
render_init_depsgraph(re);
do_render_full_pipeline(re);
@ -2204,11 +2259,15 @@ void RE_RenderAnim(Render *re,
G.is_rendering = true;
re->flag |= R_ANIMATION;
DEG_graph_id_tag_update(re->main, re->pipeline_depsgraph, &re->scene->id, ID_RECALC_AUDIO_MUTE);
scene->r.subframe = 0.0f;
for (nfra = sfra, scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) {
char filepath[FILE_MAX];
/* Reduce GPU memory usage so renderer has more space. */
RE_FreeGPUTextureCaches(false);
/* A feedback loop exists here -- render initialization requires updated
* render layers settings which could be animated, but scene evaluation for
* the frame happens later because it depends on what layers are visible to

View File

@ -39,6 +39,8 @@
#include "IMB_imbuf_types.h"
#include "IMB_openexr.h"
#include "GPU_texture.h"
#include "RE_engine.h"
#include "render_result.h"
@ -115,6 +117,18 @@ void render_result_free_list(ListBase *lb, RenderResult *rr)
}
}
void render_result_free_gpu_texture_caches(RenderResult *rr)
{
LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
if (rpass->buffer.gpu_texture) {
GPU_texture_free(rpass->buffer.gpu_texture);
rpass->buffer.gpu_texture = nullptr;
}
}
}
}
/********************************* multiview *************************************/
void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src)
@ -407,6 +421,35 @@ void RE_pass_set_buffer_data(RenderPass *pass, float *data)
RE_RenderBuffer_assign_data(&pass->buffer, data);
}
GPUTexture *RE_pass_ensure_gpu_texture_cache(Render *re, RenderPass *rpass)
{
if (rpass->buffer.gpu_texture) {
return rpass->buffer.gpu_texture;
}
if (rpass->buffer.data == nullptr) {
return nullptr;
}
const eGPUTextureFormat format = (rpass->channels == 1) ? GPU_R16F :
(rpass->channels == 3) ? GPU_RGB16F :
GPU_RGBA16F;
rpass->buffer.gpu_texture = GPU_texture_create_2d("RenderBuffer.gpu_texture",
rpass->rectx,
rpass->recty,
1,
format,
GPU_TEXTURE_USAGE_GENERAL,
NULL);
if (rpass->buffer.gpu_texture) {
GPU_texture_update(rpass->buffer.gpu_texture, GPU_DATA_FLOAT, rpass->buffer.data);
re->result_has_gpu_texture_caches = true;
}
return rpass->buffer.gpu_texture;
}
void RE_render_result_full_channel_name(char *fullname,
const char *layname,
const char *passname,
@ -1226,6 +1269,7 @@ template<class BufferType> static BufferType render_buffer_new(decltype(BufferTy
buffer.data = data;
buffer.sharing_info = blender::implicit_sharing::info_for_mem_free(data);
buffer.gpu_texture = nullptr;
return buffer;
}
@ -1238,6 +1282,11 @@ template<class BufferType> static void render_buffer_data_free(BufferType *rende
}
blender::implicit_sharing::free_shared_data(&render_buffer->data, &render_buffer->sharing_info);
if (render_buffer->gpu_texture) {
GPU_texture_free(render_buffer->gpu_texture);
render_buffer->gpu_texture = nullptr;
}
}
template<class BufferType>

View File

@ -139,6 +139,11 @@ void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResu
*/
void render_result_views_shallowdelete(struct RenderResult *rr);
/**
* Free GPU texture caches to reduce memory usage.
*/
void render_result_free_gpu_texture_caches(struct RenderResult *rr);
#define FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re_, iter_) \
{ \
int nr_; \

View File

@ -53,6 +53,8 @@ struct Render {
* write lock, all external code must use a read lock. internal code is assumed
* to not conflict with writes, so no lock used for that */
ThreadRWMutex resultmutex;
/* True if result has GPU textures, to quickly skip cache clear. */
bool result_has_gpu_texture_caches;
/* Guard for drawing render result using engine's `draw()` callback. */
ThreadMutex engine_draw_mutex;

View File

@ -83,6 +83,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "RE_pipeline.h"
/**
* When a gizmo is highlighted and uses click/drag events,
* this prevents mouse button press events from being passed through to other key-maps
@ -716,6 +718,8 @@ void wm_event_do_notifiers(bContext *C)
wm_event_do_refresh_wm_and_depsgraph(C);
RE_FreeGPUTextureCaches(true);
/* Status bar. */
if (wm->winactive) {
wmWindow *win = wm->winactive;