Compare commits
12 Commits
geometry-n
...
geometry-n
Author | SHA1 | Date | |
---|---|---|---|
f4b1c9e0c0 | |||
c8b0fcb177 | |||
54d9138a87 | |||
ac229a8f92 | |||
790cfe022d | |||
8810699abf | |||
b3e2ef7f19 | |||
47bdf81674 | |||
4437f233f7 | |||
b85bded21b | |||
3ef5ad78d0 | |||
6b7026dd04 |
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
176
source/blender/nodes/NOD_math_functions.hh
Normal file
176
source/blender/nodes/NOD_math_functions.hh
Normal 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
|
@@ -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 ¶ms)
|
||||
{
|
||||
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)
|
||||
|
117
source/blender/nodes/intern/math_functions.cc
Normal file
117
source/blender/nodes/intern/math_functions.cc
Normal 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
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user