MacOS: Enable support for EDR rendering #105662
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -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)
|
|
@ -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
|
@ -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
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 ¶ms);
|
||||
float voronoi_distance(const float3 a, const float3 b, const VoronoiParams ¶ms);
|
||||
float voronoi_distance(const float4 a, const float4 b, const VoronoiParams ¶ms);
|
||||
|
||||
/* **** 1D Voronoi **** */
|
||||
|
||||
float4 voronoi_position(const float coord);
|
||||
VoronoiOutput voronoi_f1(const VoronoiParams ¶ms, const float coord);
|
||||
VoronoiOutput voronoi_smooth_f1(const VoronoiParams ¶ms,
|
||||
const float coord,
|
||||
const bool calc_color);
|
||||
VoronoiOutput voronoi_f2(const VoronoiParams ¶ms, const float coord);
|
||||
float voronoi_distance_to_edge(const VoronoiParams ¶ms, const float coord);
|
||||
float voronoi_n_sphere_radius(const VoronoiParams ¶ms, const float coord);
|
||||
|
||||
/* **** 2D Voronoi **** */
|
||||
|
||||
float4 voronoi_position(const float2 coord);
|
||||
VoronoiOutput voronoi_f1(const VoronoiParams ¶ms, const float2 coord);
|
||||
VoronoiOutput voronoi_smooth_f1(const VoronoiParams ¶ms,
|
||||
const float2 coord,
|
||||
const bool calc_color);
|
||||
VoronoiOutput voronoi_f2(const VoronoiParams ¶ms, const float2 coord);
|
||||
float voronoi_distance_to_edge(const VoronoiParams ¶ms, const float2 coord);
|
||||
float voronoi_n_sphere_radius(const VoronoiParams ¶ms, const float2 coord);
|
||||
|
||||
/* **** 3D Voronoi **** */
|
||||
|
||||
float4 voronoi_position(const float3 coord);
|
||||
VoronoiOutput voronoi_f1(const VoronoiParams ¶ms, const float3 coord);
|
||||
VoronoiOutput voronoi_smooth_f1(const VoronoiParams ¶ms,
|
||||
const float3 coord,
|
||||
const bool calc_color);
|
||||
VoronoiOutput voronoi_f2(const VoronoiParams ¶ms, const float3 coord);
|
||||
float voronoi_distance_to_edge(const VoronoiParams ¶ms, const float3 coord);
|
||||
float voronoi_n_sphere_radius(const VoronoiParams ¶ms, const float3 coord);
|
||||
|
||||
/* **** 4D Voronoi **** */
|
||||
|
||||
float4 voronoi_position(const float4 coord);
|
||||
VoronoiOutput voronoi_f1(const VoronoiParams ¶ms, const float4 coord);
|
||||
VoronoiOutput voronoi_smooth_f1(const VoronoiParams ¶ms,
|
||||
const float4 coord,
|
||||
const bool calc_color);
|
||||
VoronoiOutput voronoi_f2(const VoronoiParams ¶ms, const float4 coord);
|
||||
float voronoi_distance_to_edge(const VoronoiParams ¶ms, const float4 coord);
|
||||
float voronoi_n_sphere_radius(const VoronoiParams ¶ms, const float4 coord);
|
||||
|
||||
/* Fractal Voronoi Noise */
|
||||
|
||||
template<typename T>
|
||||
VoronoiOutput fractal_voronoi_x_fx(const VoronoiParams ¶ms,
|
||||
const T coord,
|
||||
const bool calc_color);
|
||||
template<typename T>
|
||||
float fractal_voronoi_distance_to_edge(const VoronoiParams ¶ms, const T coord);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -8,12 +8,4 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void ED_operatortypes_io(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
void ED_operatortypes_io();
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 ¶ms)
|
|||
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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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_; \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue