Compare commits

...

12 Commits

7 changed files with 409 additions and 482 deletions

View File

@@ -152,6 +152,19 @@ class GeometryComponent {
return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
}
blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
const CustomDataType data_type,
const void *value) const;
template<typename T>
blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
const T &value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_get_constant_for_read(domain, type, &value);
}
/**
* Returns the attribute with the given parameters if it exists.
* If an exact match does not exist, other attributes with the same name are deleted and a new

View File

@@ -533,15 +533,21 @@ ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attri
if (attribute) {
return attribute;
}
return this->attribute_get_constant_for_read(domain, data_type, default_value);
}
blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read(
const AttributeDomain domain, const CustomDataType data_type, const void *value) const
{
BLI_assert(this->attribute_domain_supported(domain));
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
if (default_value == nullptr) {
default_value = cpp_type->default_value();
if (value == nullptr) {
value = cpp_type->default_value();
}
const int domain_size = this->attribute_domain_size(domain);
return std::make_unique<blender::bke::ConstantReadAttribute>(
domain, domain_size, *cpp_type, default_value);
domain, domain_size, *cpp_type, value);
}
WriteAttributePtr GeometryComponent::attribute_try_ensure_for_write(const StringRef attribute_name,

View File

@@ -274,6 +274,7 @@ set(SRC
texture/node_texture_util.c
intern/derived_node_tree.cc
intern/math_functions.cc
intern/node_common.c
intern/node_exec.c
intern/node_geometry_exec.cc
@@ -299,6 +300,7 @@ set(SRC
NOD_node_tree_ref.hh
NOD_shader.h
NOD_geometry.h
NOD_math_functions.hh
NOD_socket.h
NOD_static_types.h
NOD_texture.h

View File

@@ -0,0 +1,176 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "DNA_node_types.h"
#include "BLI_math_base_safe.h"
#include "BLI_math_rotation.h"
#include "BLI_string_ref.hh"
namespace blender::nodes {
struct FloatMathOperationInfo {
StringRefNull title_case_name;
StringRefNull shader_name;
FloatMathOperationInfo() = delete;
FloatMathOperationInfo(StringRefNull title_case_name, StringRefNull shader_name)
: title_case_name(title_case_name), shader_name(shader_name)
{
}
};
const FloatMathOperationInfo *get_float_math_operation_info(const int operation);
template<typename OpType>
inline bool try_dispatch_float_math_fl_to_fl(const int operation, OpType &&op)
{
const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
if (info == nullptr) {
return false;
}
auto dispatch = [&](auto function) -> bool {
op(function, *info);
return true;
};
switch (operation) {
case NODE_MATH_EXPONENT:
return dispatch([](float a) { return expf(a); });
case NODE_MATH_SQRT:
return dispatch([](float a) { return safe_sqrtf(a); });
case NODE_MATH_INV_SQRT:
return dispatch([](float a) { return safe_inverse_sqrtf(a); });
case NODE_MATH_ABSOLUTE:
return dispatch([](float a) { return fabs(a); });
case NODE_MATH_RADIANS:
return dispatch([](float a) { return DEG2RAD(a); });
case NODE_MATH_DEGREES:
return dispatch([](float a) { return RAD2DEG(a); });
case NODE_MATH_SIGN:
return dispatch([](float a) { return compatible_signf(a); });
case NODE_MATH_ROUND:
return dispatch([](float a) { return floorf(a + 0.5f); });
case NODE_MATH_FLOOR:
return dispatch([](float a) { return floorf(a); });
case NODE_MATH_CEIL:
return dispatch([](float a) { return ceilf(a); });
case NODE_MATH_FRACTION:
return dispatch([](float a) { return a - floorf(a); });
case NODE_MATH_TRUNC:
return dispatch([](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); });
case NODE_MATH_SINE:
return dispatch([](float a) { return sinf(a); });
case NODE_MATH_COSINE:
return dispatch([](float a) { return cosf(a); });
case NODE_MATH_TANGENT:
return dispatch([](float a) { return tanf(a); });
case NODE_MATH_SINH:
return dispatch([](float a) { return sinhf(a); });
case NODE_MATH_COSH:
return dispatch([](float a) { return coshf(a); });
case NODE_MATH_TANH:
return dispatch([](float a) { return tanhf(a); });
case NODE_MATH_ARCSINE:
return dispatch([](float a) { return safe_asinf(a); });
case NODE_MATH_ARCCOSINE:
return dispatch([](float a) { return safe_acosf(a); });
case NODE_MATH_ARCTANGENT:
return dispatch([](float a) { return atanf(a); });
}
return false;
}
template<typename OpType>
inline bool try_dispatch_float_math_fl_fl_to_fl(const int operation, OpType &&op)
{
const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
if (info == nullptr) {
return false;
}
auto dispatch = [&](auto function) -> bool {
op(function, *info);
return true;
};
switch (operation) {
case NODE_MATH_ADD:
return dispatch([](float a, float b) { return a + b; });
case NODE_MATH_SUBTRACT:
return dispatch([](float a, float b) { return a - b; });
case NODE_MATH_MULTIPLY:
return dispatch([](float a, float b) { return a * b; });
case NODE_MATH_DIVIDE:
return dispatch([](float a, float b) { return safe_divide(a, b); });
case NODE_MATH_POWER:
return dispatch([](float a, float b) { return safe_powf(a, b); });
case NODE_MATH_LOGARITHM:
return dispatch([](float a, float b) { return safe_logf(a, b); });
case NODE_MATH_MINIMUM:
return dispatch([](float a, float b) { return std::min(a, b); });
case NODE_MATH_MAXIMUM:
return dispatch([](float a, float b) { return std::max(a, b); });
case NODE_MATH_LESS_THAN:
return dispatch([](float a, float b) { return (float)(a < b); });
case NODE_MATH_GREATER_THAN:
return dispatch([](float a, float b) { return (float)(a > b); });
case NODE_MATH_MODULO:
return dispatch([](float a, float b) { return safe_modf(a, b); });
case NODE_MATH_SNAP:
return dispatch([](float a, float b) { return floorf(safe_divide(a, b)) * b; });
case NODE_MATH_ARCTAN2:
return dispatch([](float a, float b) { return atan2f(a, b); });
case NODE_MATH_PINGPONG:
return dispatch([](float a, float b) { return pingpongf(a, b); });
}
return false;
}
template<typename OpType>
inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, OpType &&op)
{
const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
if (info == nullptr) {
return false;
}
auto dispatch = [&](auto function) -> bool {
op(function, *info);
return true;
};
switch (operation) {
case NODE_MATH_MULTIPLY_ADD:
return dispatch([](float a, float b, float c) { return a * b + c; });
case NODE_MATH_COMPARE:
return dispatch([](float a, float b, float c) -> float {
return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
});
case NODE_MATH_SMOOTH_MIN:
return dispatch([](float a, float b, float c) { return smoothminf(a, b, c); });
case NODE_MATH_SMOOTH_MAX:
return dispatch([](float a, float b, float c) { return -smoothminf(-a, -b, -c); });
case NODE_MATH_WRAP:
return dispatch([](float a, float b, float c) { return wrapf(a, b, c); });
}
return false;
}
} // namespace blender::nodes

View File

@@ -26,6 +26,8 @@
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "NOD_math_functions.hh"
static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute A")},
@@ -64,221 +66,70 @@ static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *no
namespace blender::nodes {
/**
* Do implicit conversion from other attribute types to float.
*/
static Array<float> attribute_ensure_float_type(ReadAttributePtr attribute)
static void do_math_operation(const FloatReadAttribute &input_a,
const FloatReadAttribute &input_b,
FloatWriteAttribute result,
const int operation)
{
Array<float> array(attribute->size());
if (attribute->cpp_type().is<float>()) {
FloatReadAttribute float_attribute = std::move(attribute);
for (const int i : IndexRange(float_attribute.size())) {
array[i] = float_attribute[i];
}
}
else if (attribute->cpp_type().is<float3>()) {
Float3ReadAttribute float3_attribute = std::move(attribute);
for (const int i : IndexRange(float3_attribute.size())) {
array[i] = float3_attribute[i].length();
}
}
#if 0 /* More CPP types to support in the future. */
else if (attribute->cpp_type().is<Color4f>()) {
}
else if (attribute->cpp_type().is<float2>()) {
}
else if (attribute->cpp_type().is<int>()) {
}
#endif
const int size = input_a.size();
return array;
}
static void math_operation_attribute_float(Array<float> input_a,
float input_b,
FloatWriteAttribute float_attribute,
const int operation)
{
switch (operation) {
case NODE_MATH_ADD:
for (const int i : IndexRange(input_a.size())) {
float_attribute.set(i, input_a[i] + input_b);
}
break;
case NODE_MATH_SUBTRACT:
for (const int i : IndexRange(input_a.size())) {
float_attribute.set(i, input_a[i] - input_b);
}
break;
case NODE_MATH_MULTIPLY:
for (const int i : IndexRange(input_a.size())) {
float_attribute.set(i, input_a[i] * input_b);
}
break;
case NODE_MATH_DIVIDE:
/* Protect against division by zero. */
if (input_b == 0.0f) {
for (const int i : IndexRange(input_a.size())) {
float_attribute.set(i, 0.0f);
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float in1 = input_a[i];
const float in2 = input_b[i];
const float out = math_function(in1, in2);
result.set(i, out);
}
}
else {
for (const int i : IndexRange(input_a.size())) {
float_attribute.set(i, input_a[i] / input_b);
}
}
break;
}
}
});
static void math_operation_float_attribute(float input_a,
Array<float> input_b,
FloatWriteAttribute float_attribute,
const int operation)
{
switch (operation) {
case NODE_MATH_ADD:
for (const int i : IndexRange(float_attribute.size())) {
float_attribute.set(i, input_a + input_b[i]);
}
break;
case NODE_MATH_SUBTRACT:
for (const int i : IndexRange(float_attribute.size())) {
float_attribute.set(i, input_a - input_b[i]);
}
break;
case NODE_MATH_MULTIPLY:
for (const int i : IndexRange(float_attribute.size())) {
float_attribute.set(i, input_a * input_b[i]);
}
break;
case NODE_MATH_DIVIDE:
for (const int i : IndexRange(float_attribute.size())) {
/* Protect against division by zero. */
float_attribute.set(i, safe_divide(input_a, input_b[i]));
}
break;
}
}
static void math_operation_attribute_attribute(Array<float> input_a,
Array<float> input_b,
FloatWriteAttribute float_attribute,
const int operation)
{
switch (operation) {
case NODE_MATH_ADD:
for (const int i : IndexRange(float_attribute.size())) {
float_attribute.set(i, input_a[i] + input_b[i]);
}
break;
case NODE_MATH_SUBTRACT:
for (const int i : IndexRange(float_attribute.size())) {
float_attribute.set(i, input_a[i] - input_b[i]);
}
break;
case NODE_MATH_MULTIPLY:
for (const int i : IndexRange(float_attribute.size())) {
float_attribute.set(i, input_a[i] * input_b[i]);
}
break;
case NODE_MATH_DIVIDE:
for (const int i : IndexRange(float_attribute.size())) {
/* Protect against division by zero. */
float_attribute.set(i, safe_divide(input_a[i], input_b[i]));
}
break;
}
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams &params)
{
const bNode &node = params.node();
const int operation = params.node().custom1;
const std::string result_name = params.get_input<std::string>("Result");
const int operation = node.custom1;
/* The result type of this node is always float. */
const CustomDataType result_type = bke::cpp_type_to_custom_data_type(CPPType::get<float>());
const CustomDataType result_type = CD_PROP_FLOAT;
/* The result domain is always point for now. */
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
/* Use a single float for none or one of the inputs, which leads to three cases,
* because the order of the inputs in the operations can matter. */
GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node.custom2);
if ((flag & GEO_NODE_USE_ATTRIBUTE_A) && (flag & GEO_NODE_USE_ATTRIBUTE_B)) {
/* Two attributes. */
const std::string attribute_a_name = params.get_input<std::string>("Attribute A");
const std::string attribute_b_name = params.get_input<std::string>("Attribute B");
ReadAttributePtr attribute_a = component.attribute_try_get_for_read(attribute_a_name);
ReadAttributePtr attribute_b = component.attribute_try_get_for_read(attribute_b_name);
if (!attribute_a || !attribute_b) {
return;
}
/* Don't handle domain interpolation for now. */
AttributeDomain domain_a = attribute_a->domain();
AttributeDomain domain_b = attribute_b->domain();
if (domain_a != domain_b) {
return;
}
BLI_assert(attribute_a->size() == attribute_b->size());
Array<float> input_a = attribute_ensure_float_type(std::move(attribute_a));
Array<float> input_b = attribute_ensure_float_type(std::move(attribute_b));
WriteAttributePtr result = component.attribute_try_ensure_for_write(
result_name, domain_a, result_type);
if (!result) {
return;
}
math_operation_attribute_attribute(input_a, input_b, std::move(result), operation);
}
else if (flag & GEO_NODE_USE_ATTRIBUTE_A) {
/* Attribute and float. */
const std::string attribute_a_name = params.get_input<std::string>("Attribute A");
ReadAttributePtr attribute_a = component.attribute_try_get_for_read(attribute_a_name);
if (!attribute_a) {
return;
}
AttributeDomain domain = attribute_a->domain();
Array<float> input_a = attribute_ensure_float_type(std::move(attribute_a));
const float input_b = params.get_input<float>("B");
WriteAttributePtr result = component.attribute_try_ensure_for_write(
result_name, domain, result_type);
if (!result) {
return;
}
math_operation_attribute_float(input_a, input_b, std::move(result), operation);
}
else if (flag & GEO_NODE_USE_ATTRIBUTE_B) {
/* Float and attribute. */
const std::string attribute_b_name = params.get_input<std::string>("Attribute B");
ReadAttributePtr attribute_b = component.attribute_try_get_for_read(attribute_b_name);
if (!attribute_b) {
return;
}
AttributeDomain domain = attribute_b->domain();
Array<float> input_b = attribute_ensure_float_type(std::move(attribute_b));
const float input_a = params.get_input<float>("A");
WriteAttributePtr result = component.attribute_try_ensure_for_write(
result_name, domain, result_type);
if (!result) {
return;
}
math_operation_float_attribute(input_a, input_b, std::move(result), operation);
}
else {
/* There was no attribute provided, so just return. Otherwise choosing which domain to use
* for a new attribute would be completely arbitrary, and we don't want to make those sort
* of decisions in the attribute math node. */
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
const std::string result_name = params.get_input<std::string>("Result");
WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node.custom2);
auto get_input_attribute = [&](GeometryNodeUseAttributeFlag use_flag,
StringRef attribute_socket_identifier,
StringRef value_socket_identifier) {
if (flag & use_flag) {
const std::string attribute_name = params.get_input<std::string>(
attribute_socket_identifier);
return component.attribute_try_get_for_read(attribute_name, result_domain, result_type);
}
const float value = params.get_input<float>(value_socket_identifier);
return component.attribute_get_constant_for_read(result_domain, result_type, &value);
};
ReadAttributePtr attribute_a = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_A, "Attribute A", "A");
ReadAttributePtr attribute_b = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_B, "Attribute B", "B");
if (!attribute_a || !attribute_b) {
/* Attribute wasn't found. */
return;
}
do_math_operation(
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)

View File

@@ -0,0 +1,117 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "NOD_math_functions.hh"
namespace blender::nodes {
const FloatMathOperationInfo *get_float_math_operation_info(const int operation)
{
#define RETURN_OPERATION_INFO(title_case_name, shader_name) \
{ \
static const FloatMathOperationInfo info{title_case_name, shader_name}; \
return &info; \
} \
((void)0)
switch (operation) {
case NODE_MATH_ADD:
RETURN_OPERATION_INFO("Add", "math_add");
case NODE_MATH_SUBTRACT:
RETURN_OPERATION_INFO("Subtract", "math_subtract");
case NODE_MATH_MULTIPLY:
RETURN_OPERATION_INFO("Multiply", "math_multiply");
case NODE_MATH_DIVIDE:
RETURN_OPERATION_INFO("Divide", "math_divide");
case NODE_MATH_SINE:
RETURN_OPERATION_INFO("Sine", "math_sine");
case NODE_MATH_COSINE:
RETURN_OPERATION_INFO("Cosine", "math_cosine");
case NODE_MATH_ARCSINE:
RETURN_OPERATION_INFO("Arc Sine", "math_arcsine");
case NODE_MATH_ARCCOSINE:
RETURN_OPERATION_INFO("Arc Cosine", "math_arccosine");
case NODE_MATH_ARCTANGENT:
RETURN_OPERATION_INFO("Arc Tangent", "math_arctangent");
case NODE_MATH_POWER:
RETURN_OPERATION_INFO("Power", "math_power");
case NODE_MATH_LOGARITHM:
RETURN_OPERATION_INFO("Logarithm", "math_logarithm");
case NODE_MATH_MINIMUM:
RETURN_OPERATION_INFO("Minimum", "math_minimum");
case NODE_MATH_MAXIMUM:
RETURN_OPERATION_INFO("Maximum", "math_maximum");
case NODE_MATH_ROUND:
RETURN_OPERATION_INFO("Round", "math_round");
case NODE_MATH_LESS_THAN:
RETURN_OPERATION_INFO("Less Than", "math_less_than");
case NODE_MATH_GREATER_THAN:
RETURN_OPERATION_INFO("Greater Than", "math_greater_than");
case NODE_MATH_MODULO:
RETURN_OPERATION_INFO("Modulo", "math_modulo");
case NODE_MATH_ABSOLUTE:
RETURN_OPERATION_INFO("Absolute", "math_absolute");
case NODE_MATH_ARCTAN2:
RETURN_OPERATION_INFO("Arc Tangent 2", "math_arctan2");
case NODE_MATH_FLOOR:
RETURN_OPERATION_INFO("Floor", "math_floor");
case NODE_MATH_CEIL:
RETURN_OPERATION_INFO("Ceil", "math_ceil");
case NODE_MATH_FRACTION:
RETURN_OPERATION_INFO("Fraction", "math_fraction");
case NODE_MATH_SQRT:
RETURN_OPERATION_INFO("Sqrt", "math_sqrt");
case NODE_MATH_INV_SQRT:
RETURN_OPERATION_INFO("Inverse Sqrt", "math_insersesqrt");
case NODE_MATH_SIGN:
RETURN_OPERATION_INFO("Sign", "math_sign");
case NODE_MATH_EXPONENT:
RETURN_OPERATION_INFO("Exponent", "math_exponent");
case NODE_MATH_RADIANS:
RETURN_OPERATION_INFO("Radians", "math_radians");
case NODE_MATH_DEGREES:
RETURN_OPERATION_INFO("Degrees", "math_degrees");
case NODE_MATH_SINH:
RETURN_OPERATION_INFO("Hyperbolic Sine", "math_sinh");
case NODE_MATH_COSH:
RETURN_OPERATION_INFO("Hyperbolic Cosine", "math_cosh");
case NODE_MATH_TANH:
RETURN_OPERATION_INFO("Hyperbolic Tangent", "math_tanh");
case NODE_MATH_TRUNC:
RETURN_OPERATION_INFO("Truncate", "math_trunc");
case NODE_MATH_SNAP:
RETURN_OPERATION_INFO("Snap", "math_snap");
case NODE_MATH_WRAP:
RETURN_OPERATION_INFO("Wrap", "math_wrap");
case NODE_MATH_COMPARE:
RETURN_OPERATION_INFO("Compare", "math_compare");
case NODE_MATH_MULTIPLY_ADD:
RETURN_OPERATION_INFO("Multiply Add", "math_multiply_add");
case NODE_MATH_PINGPONG:
RETURN_OPERATION_INFO("Ping Pong", "math_pingpong");
case NODE_MATH_SMOOTH_MIN:
RETURN_OPERATION_INFO("Smooth Min", "math_smoothmin");
case NODE_MATH_SMOOTH_MAX:
RETURN_OPERATION_INFO("Smooth Max", "math_smoothmax");
}
#undef RETURN_OPERATION_INFO
return nullptr;
}
} // namespace blender::nodes

View File

@@ -23,6 +23,8 @@
#include "node_shader_util.h"
#include "NOD_math_functions.hh"
/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate sh_node_math_in[] = {
{SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
@@ -34,93 +36,15 @@ static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1,
static const char *gpu_shader_get_name(int mode)
{
switch (mode) {
case NODE_MATH_ADD:
return "math_add";
case NODE_MATH_SUBTRACT:
return "math_subtract";
case NODE_MATH_MULTIPLY:
return "math_multiply";
case NODE_MATH_DIVIDE:
return "math_divide";
case NODE_MATH_MULTIPLY_ADD:
return "math_multiply_add";
case NODE_MATH_POWER:
return "math_power";
case NODE_MATH_LOGARITHM:
return "math_logarithm";
case NODE_MATH_EXPONENT:
return "math_exponent";
case NODE_MATH_SQRT:
return "math_sqrt";
case NODE_MATH_INV_SQRT:
return "math_inversesqrt";
case NODE_MATH_ABSOLUTE:
return "math_absolute";
case NODE_MATH_RADIANS:
return "math_radians";
case NODE_MATH_DEGREES:
return "math_degrees";
case NODE_MATH_MINIMUM:
return "math_minimum";
case NODE_MATH_MAXIMUM:
return "math_maximum";
case NODE_MATH_LESS_THAN:
return "math_less_than";
case NODE_MATH_GREATER_THAN:
return "math_greater_than";
case NODE_MATH_SIGN:
return "math_sign";
case NODE_MATH_COMPARE:
return "math_compare";
case NODE_MATH_SMOOTH_MIN:
return "math_smoothmin";
case NODE_MATH_SMOOTH_MAX:
return "math_smoothmax";
case NODE_MATH_ROUND:
return "math_round";
case NODE_MATH_FLOOR:
return "math_floor";
case NODE_MATH_CEIL:
return "math_ceil";
case NODE_MATH_FRACTION:
return "math_fraction";
case NODE_MATH_MODULO:
return "math_modulo";
case NODE_MATH_TRUNC:
return "math_trunc";
case NODE_MATH_SNAP:
return "math_snap";
case NODE_MATH_WRAP:
return "math_wrap";
case NODE_MATH_PINGPONG:
return "math_pingpong";
case NODE_MATH_SINE:
return "math_sine";
case NODE_MATH_COSINE:
return "math_cosine";
case NODE_MATH_TANGENT:
return "math_tangent";
case NODE_MATH_SINH:
return "math_sinh";
case NODE_MATH_COSH:
return "math_cosh";
case NODE_MATH_TANH:
return "math_tanh";
case NODE_MATH_ARCSINE:
return "math_arcsine";
case NODE_MATH_ARCCOSINE:
return "math_arccosine";
case NODE_MATH_ARCTANGENT:
return "math_arctangent";
case NODE_MATH_ARCTAN2:
return "math_arctan2";
const blender::nodes::FloatMathOperationInfo *info =
blender::nodes::get_float_math_operation_info(mode);
if (!info) {
return nullptr;
}
return nullptr;
if (info->shader_name.is_empty()) {
return nullptr;
}
return info->shader_name.c_str();
}
static int gpu_shader_math(GPUMaterial *mat,
@@ -149,201 +73,39 @@ static const blender::fn::MultiFunction &get_base_multi_function(
blender::nodes::NodeMFNetworkBuilder &builder)
{
const int mode = builder.bnode().custom1;
switch (mode) {
case NODE_MATH_ADD: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Add", [](float a, float b) { return a + b; }};
return fn;
}
case NODE_MATH_SUBTRACT: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Subtract", [](float a, float b) { return a - b; }};
return fn;
}
case NODE_MATH_MULTIPLY: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Multiply", [](float a, float b) { return a * b; }};
return fn;
}
case NODE_MATH_DIVIDE: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Divide", safe_divide};
return fn;
}
case NODE_MATH_MULTIPLY_ADD: {
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
"Multiply Add", [](float a, float b, float c) { return a * b + c; }};
return fn;
}
case NODE_MATH_POWER: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Power", safe_powf};
return fn;
}
case NODE_MATH_LOGARITHM: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Logarithm", safe_logf};
return fn;
}
case NODE_MATH_EXPONENT: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Exponent", expf};
return fn;
}
case NODE_MATH_SQRT: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Sqrt", safe_sqrtf};
return fn;
}
case NODE_MATH_INV_SQRT: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Inverse Sqrt", safe_inverse_sqrtf};
return fn;
};
case NODE_MATH_ABSOLUTE: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Absolute",
[](float a) { return fabs(a); }};
return fn;
}
case NODE_MATH_RADIANS: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Radians",
[](float a) { return DEG2RAD(a); }};
return fn;
}
case NODE_MATH_DEGREES: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Degrees",
[](float a) { return RAD2DEG(a); }};
return fn;
}
const blender::fn::MultiFunction *base_fn = nullptr;
case NODE_MATH_MINIMUM: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Minimum", [](float a, float b) { return std::min(a, b); }};
return fn;
}
case NODE_MATH_MAXIMUM: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Maximum", [](float a, float b) { return std::max(a, b); }};
return fn;
}
case NODE_MATH_LESS_THAN: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Less Than", [](float a, float b) { return (float)(a < b); }};
return fn;
}
case NODE_MATH_GREATER_THAN: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Greater Than", [](float a, float b) { return (float)(a > b); }};
return fn;
}
case NODE_MATH_SIGN: {
static blender::fn::CustomMF_SI_SO<float, float> fn{
"Sign", [](float a) { return compatible_signf(a); }};
return fn;
}
case NODE_MATH_COMPARE: {
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
"Compare", [](float a, float b, float c) -> float {
return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
}};
return fn;
}
case NODE_MATH_SMOOTH_MIN: {
return builder.get_not_implemented_fn();
}
case NODE_MATH_SMOOTH_MAX: {
return builder.get_not_implemented_fn();
}
case NODE_MATH_ROUND: {
static blender::fn::CustomMF_SI_SO<float, float> fn{
"Round", [](float a) { return floorf(a + 0.5f); }};
return fn;
}
case NODE_MATH_FLOOR: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Floor",
[](float a) { return floorf(a); }};
return fn;
}
case NODE_MATH_CEIL: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Ceil",
[](float a) { return ceilf(a); }};
return fn;
}
case NODE_MATH_FRACTION: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Fraction",
[](float a) { return a - floorf(a); }};
return fn;
}
case NODE_MATH_MODULO: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Modulo", [](float a, float b) { return safe_modf(a, b); }};
return fn;
}
case NODE_MATH_TRUNC: {
static blender::fn::CustomMF_SI_SO<float, float> fn{
"Trunc", [](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); }};
return fn;
}
case NODE_MATH_SNAP: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Snap", [](float a, float b) { return floorf(safe_divide(a, b)) * b; }};
return fn;
}
case NODE_MATH_WRAP: {
return builder.get_not_implemented_fn();
}
case NODE_MATH_PINGPONG: {
return builder.get_not_implemented_fn();
}
case NODE_MATH_SINE: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Sine", [](float a) { return sinf(a); }};
return fn;
}
case NODE_MATH_COSINE: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Cosine",
[](float a) { return cosf(a); }};
return fn;
}
case NODE_MATH_TANGENT: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Tangent",
[](float a) { return tanf(a); }};
return fn;
}
case NODE_MATH_SINH: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Sine",
[](float a) { return sinhf(a); }};
return fn;
}
case NODE_MATH_COSH: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Cosine",
[](float a) { return coshf(a); }};
return fn;
}
case NODE_MATH_TANH: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Tangent",
[](float a) { return tanhf(a); }};
return fn;
}
case NODE_MATH_ARCSINE: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Sine", safe_asinf};
return fn;
}
case NODE_MATH_ARCCOSINE: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Cosine", safe_acosf};
return fn;
}
case NODE_MATH_ARCTANGENT: {
static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Tangent",
[](float a) { return atanf(a); }};
return fn;
}
case NODE_MATH_ARCTAN2: {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
"Arc Tangent 2", [](float a, float b) { return atan2f(a, b); }};
return fn;
}
default:
BLI_assert(false);
return builder.get_not_implemented_fn();
blender::nodes::try_dispatch_float_math_fl_to_fl(
mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
static blender::fn::CustomMF_SI_SO<float, float> fn{info.title_case_name, function};
base_fn = &fn;
});
if (base_fn != nullptr) {
return *base_fn;
}
blender::nodes::try_dispatch_float_math_fl_fl_to_fl(
mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{info.title_case_name,
function};
base_fn = &fn;
});
if (base_fn != nullptr) {
return *base_fn;
}
blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl(
mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
info.title_case_name, function};
base_fn = &fn;
});
if (base_fn != nullptr) {
return *base_fn;
}
return builder.get_not_implemented_fn();
}
static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)