Files
test2/intern/cycles/kernel/osl/shaders/node_math.h
Hoshinova b880485492 Nodes: add Floored Modulo mode to Math nodes
Both the `Math` node and the `Vector Math` currently only explicitly
support modulo using truncated division which is oftentimes not the
type of modulo desired as it behaves differently for negative numbers
and positive numbers.

Floored Modulo can be created by either using the `Wrap` operation or
a combination of multiple `Math` nodes. However both methods obfuscate
the actual intend of the artist and the math operation that is actually
used.

This patch adds modulo using floored division to the scalar `Math` node,
explicitly stating the intended math operation and renames the already
existing `"Modulo"` operation to `"Truncated Modulo"` to avoid confusion.
Only the ui name is changed, so this should not break compatibility.

Pull Request: https://projects.blender.org/blender/blender/pulls/110728
2023-08-08 12:13:00 +02:00

111 lines
2.5 KiB
C

/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
float safe_divide(float a, float b)
{
return (b != 0.0) ? a / b : 0.0;
}
vector safe_divide(vector a, vector b)
{
return vector((b[0] != 0.0) ? a[0] / b[0] : 0.0,
(b[1] != 0.0) ? a[1] / b[1] : 0.0,
(b[2] != 0.0) ? a[2] / b[2] : 0.0);
}
float safe_modulo(float a, float b)
{
return (b != 0.0) ? fmod(a, b) : 0.0;
}
float safe_floored_modulo(float a, float b)
{
return (b != 0.0) ? a - floor(a / b) * b : 0.0;
}
float fract(float a)
{
return a - floor(a);
}
/* See: https://www.iquilezles.org/www/articles/smin/smin.htm. */
float smoothmin(float a, float b, float c)
{
if (c != 0.0) {
float h = max(c - abs(a - b), 0.0) / c;
return min(a, b) - h * h * h * c * (1.0 / 6.0);
}
else {
return min(a, b);
}
}
float pingpong(float a, float b)
{
return (b != 0.0) ? abs(fract((a - b) / (b * 2.0)) * b * 2.0 - b) : 0.0;
}
float safe_sqrt(float a)
{
return (a > 0.0) ? sqrt(a) : 0.0;
}
float safe_log(float a, float b)
{
return (a > 0.0 && b > 0.0) ? log(a) / log(b) : 0.0;
}
vector project(vector v, vector v_proj)
{
float lenSquared = dot(v_proj, v_proj);
return (lenSquared != 0.0) ? (dot(v, v_proj) / lenSquared) * v_proj : vector(0.0);
}
vector snap(vector a, vector b)
{
return floor(safe_divide(a, b)) * b;
}
/* Adapted from GODOT-engine math_funcs.h. */
float wrap(float value, float max, float min)
{
float range = max - min;
return (range != 0.0) ? value - (range * floor((value - min) / range)) : min;
}
point wrap(point value, point max, point min)
{
return point(wrap(value[0], max[0], min[0]),
wrap(value[1], max[1], min[1]),
wrap(value[2], max[2], min[2]));
}
/* Built in OSL faceforward is `(dot(I, Nref) > 0) ? -N : N;` which is different to
* GLSL `dot(Nref, I) < 0 ? N : -N` for zero values. */
point compatible_faceforward(point vec, point incident, point reference)
{
return dot(reference, incident) < 0.0 ? vec : -vec;
}
matrix euler_to_mat(point euler)
{
float cx = cos(euler[0]);
float cy = cos(euler[1]);
float cz = cos(euler[2]);
float sx = sin(euler[0]);
float sy = sin(euler[1]);
float sz = sin(euler[2]);
matrix mat = matrix(1.0);
mat[0][0] = cy * cz;
mat[0][1] = cy * sz;
mat[0][2] = -sy;
mat[1][0] = sy * sx * cz - cx * sz;
mat[1][1] = sy * sx * sz + cx * cz;
mat[1][2] = cy * sx;
+mat[2][0] = sy * cx * cz + sx * sz;
mat[2][1] = sy * cx * sz - sx * cz;
mat[2][2] = cy * cx;
return mat;
}