GOOENGINE: Hex Grid node

Patches originally written by Joseph and Thorn. Thank you!

Implements a hex grid texture node
This commit is contained in:
2025-10-17 16:22:12 -05:00
parent a049486fac
commit 7d81a8b19e
14 changed files with 716 additions and 0 deletions
@@ -574,6 +574,7 @@ class NODE_MT_category_GEO_TEXTURE(Menu):
node_add_menu.add_node_type(layout, "ShaderNodeTexVoronoi")
node_add_menu.add_node_type(layout, "ShaderNodeTexWave")
node_add_menu.add_node_type(layout, "ShaderNodeTexWhiteNoise")
node_add_menu.add_node_type(layout, "ShaderNodeTexHexagon")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
@@ -309,6 +309,7 @@ class NODE_MT_category_shader_texture(Menu):
node_add_menu.add_node_type(layout, "ShaderNodeTexVoronoi")
node_add_menu.add_node_type(layout, "ShaderNodeTexWave")
node_add_menu.add_node_type(layout, "ShaderNodeTexWhiteNoise")
node_add_menu.add_node_type(layout, "ShaderNodeTexHexagon")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
+5
View File
@@ -1008,6 +1008,11 @@ void node_tree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index);
#define SH_NODE_SET_DEPTH 806
#define SH_NODE_CURVATURE 807
/* Fruitbat Nodes */
#define SH_NODE_COLOR_PALETTE 900
#define SH_NODE_LIGHT_INFO 901
#define SH_NODE_TEX_HEXAGON 902
/** \} */
/* -------------------------------------------------------------------- */
+1
View File
@@ -628,6 +628,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_tex_sky.glsl
shaders/material/gpu_shader_material_texture_coordinates.glsl
shaders/material/gpu_shader_material_tex_voronoi.glsl
shaders/material/gpu_shader_material_tex_hexagon.glsl
shaders/material/gpu_shader_material_tex_wave.glsl
shaders/material/gpu_shader_material_tex_white_noise.glsl
shaders/material/gpu_shader_material_toon.glsl
@@ -7,6 +7,11 @@
/* WORKAROUND: To be removed once we port all code to use `gpu_shader_math_base_lib.glsl`. */
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
float safe_floored_modulo(float a, float b, float c)
{
return (b != 0.0) ? a - floor(a / b) * b : 0.0;
}
float safe_divide(float a, float b)
{
return (b != 0.0) ? a / b : 0.0;
@@ -27,6 +27,10 @@ float pow2f(float x)
{
return x * x;
}
float safe_floored_modulo(float a, float b, float c)
{
return (b != 0.0) ? a - floor(a / b) * b : 0.0;
}
float pow3f(float x)
{
return x * x * x;
@@ -0,0 +1,156 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
#define HRATIO 1.1547005
#define HSQRT3 1.7320508
#define HSQRT2 1.4142136
#define HORIZONTAL 0
#define VERTICAL 1
#define HORIZONTAL_TILED 2
#define VERTICAL_TILED 3
/*
* SDF Functions based on:
* -
* https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
*/
float sdf_dimension(float w, inout float round)
{
float sw = sign(w);
w = abs(w);
round = mix(0.0, w, clamp(round, 0.0, 1.0));
float dim = max(w - round, 0.0);
round *= 0.5;
return dim * sw;
}
float hex_value_sdf(vec3 pos, float r, float rd)
{
vec2 p = pos.xy;
r = sdf_dimension(r, rd);
const vec3 k = vec3(HSQRT3 * -0.5, 0.5, HRATIO * 0.5);
p = abs(p);
p -= 2.0 * min(dot(k.xy, p.xy), 0.0) * k.xy;
p -= vec2(clamp(p.x, -k.z * r, k.z * r), r);
return length(p) * sign(p.y) - rd * 2.0;
}
float hex_value(vec3 hp, float radius)
{
vec3 fac = vec3(abs(hp.x - hp.y), abs(hp.y - hp.z), abs(hp.z - hp.x));
float f = max(fac.x, max(fac.y, fac.z));
return (radius == 0.0) ? f : mix(f, length(fac) / HSQRT2, radius);
}
vec3 xy_to_hex(vec3 xy, float ratio)
{
vec3 p = xy;
p.x *= ratio;
p.z = -0.5 * p.x - p.y;
p.y = -0.5 * p.x + p.y;
return p;
}
float compatible_mod(float a, float b)
{
return (b != 0.0 && a != b) ? a - b * floor(a / b) : 0.0;
}
float hexagon(vec3 p,
float scale,
float size,
float radius,
float roundness,
int coord_mode,
int value_mode,
int direction,
out vec4 cell_color,
out vec3 hex_coords,
out vec3 grid_position,
out vec3 cell_coords,
out vec3 cell_id)
{
float ratio = (direction == HORIZONTAL_TILED || direction == VERTICAL_TILED) ? 1.0 : HRATIO;
if (direction == VERTICAL || direction == VERTICAL_TILED) {
p = p.yxz;
}
p = xy_to_hex(p * scale, ratio);
hex_coords = p;
vec3 ip = floor(p + 0.5);
float s = ip.x + ip.y + ip.z;
vec3 abs_d = vec3(0.0);
if (s != 0.0) {
abs_d = abs(ip - p);
if (abs_d.x >= abs_d.y && abs_d.x >= abs_d.z) {
ip.x -= s;
}
else if (abs_d.y >= abs_d.x && abs_d.y >= abs_d.z) {
ip.y -= s;
}
else {
ip.z -= s;
}
}
/* Fix possible negative zero issue. (ip.z = (ip.z == 0.0) ? 0.0 : ip.z;) */
vec3 hp = p - ip;
hp *= (size != 0.0) ? 1.0 / size : 0.0;
vec3 xy_coords = vec3(hp.x * HSQRT3, hp.y - hp.z, 0.0);
if (coord_mode == 1) {
cell_coords = hp;
cell_id = ip;
}
else {
cell_coords = xy_coords;
cell_id = vec3(ip.x / ratio, (ip.y - ip.z + (1.0 - compatible_mod(ip.x, 2.0))) / 2.0, 0.0);
}
if (direction == VERTICAL || direction == VERTICAL_TILED) {
hp = hp.yxz;
cell_coords = cell_coords.yxz;
cell_id = cell_id.yxz;
}
grid_position = safe_divide(cell_id, vec3(scale));
cell_color.xyz = hash_vec3_to_vec3(cell_id);
/* Calc value. */
if (value_mode == 2) { /* SHD_HEXAGON_VALUE_DOT */
return length(hp);
}
else if (value_mode == 1) { /* SHD_HEXAGON_VALUE_SDF */
return hex_value_sdf(xy_coords, radius, roundness);
}
else { /* NODE_HEXAGON_VALUE_HEX */
return hex_value(hp, radius);
}
}
void node_tex_hexagon(vec3 co,
float scale,
float size,
float radius,
float roundness,
float coord_mode,
float value_mode,
float direction,
out float value,
out vec4 cell_color,
out vec3 coords,
out vec3 position,
out vec3 cell_coords,
out vec3 cell)
{
value = hexagon(co,
scale,
size,
radius,
roundness,
int(coord_mode),
int(value_mode),
int(direction),
cell_color,
coords,
position,
cell_coords,
cell);
}
+20
View File
@@ -1530,6 +1530,14 @@ typedef struct NodeShaderOutputAOV {
char name[64];
} NodeShaderOutputAOV;
typedef struct NodeTexHexagon {
NodeTexBase base;
int coord_mode;
int value_mode;
int direction;
int use_clamp;
} NodeTexHexagon;
typedef struct NodeSunBeams {
float source[2];
@@ -2392,6 +2400,18 @@ enum {
SHD_SKY_NISHITA = 2,
};
/* Hexagon node coords. */
enum {
SHD_HEXAGON_COORDS_XY = 0,
SHD_HEXAGON_COORDS_HEX = 1,
};
/* Hexagon node value mode. */
enum {
SHD_HEXAGON_VALUE_HEX = 0,
SHD_HEXAGON_VALUE_SDF = 1,
SHD_HEXAGON_VALUE_DOT = 2,
};
/* environment texture */
enum {
SHD_PROJ_EQUIRECTANGULAR = 0,
@@ -5778,6 +5778,70 @@ static void def_sh_shader_info(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_sh_tex_hexagon(StructRNA *srna)
{
static const EnumPropertyItem prop_hexagon_coords_items[] = {
{SHD_HEXAGON_COORDS_XY,
"XY",
0,
"XY Position",
"Cell ID pattern and UV coordinates output XY values"},
{SHD_HEXAGON_COORDS_HEX,
"HEX",
0,
"Hex Position",
"Cell ID pattern and UV coordinates output HEX values"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem prop_hexagon_value_items[] = {
{SHD_HEXAGON_VALUE_HEX, "HEX", 0, "Hexagons", "Value based on hexagon distance function"},
{SHD_HEXAGON_VALUE_SDF,
"SDF",
0,
"SDF Hexagons",
"Value based on sdf hexagon distance function"},
{SHD_HEXAGON_VALUE_DOT, "DOT", 0, "Dots", "Value based on coordinate length"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem prop_hexagon_direction_items[] = {
{0, "HORIZONTAL", 0, "Horizontal", "Hexagons point horizontally"},
{1, "VERTICAL", 0, "Vertical", "Hexagons point vertically"},
{2,
"HORIZONTAL_TILED",
0,
"Horizontal Tiled",
"Hexagons point horizontally with an aspect ratio to fit hexagon into a square"},
{3,
"VERTICAL_TILED",
0,
"Vertical Tiled",
"Hexagons point vertically with an aspect ratio to fit hexagon into a square"},
{0, nullptr, 0, nullptr, nullptr},
};
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeTexHexagon", "storage");
def_sh_tex(srna);
prop = RNA_def_property(srna, "coord_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "coord_mode");
RNA_def_property_enum_items(prop, prop_hexagon_coords_items);
RNA_def_property_ui_text(prop, "Coordinate Mode", "Output XY or Hex coordinates");
RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "value_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "value_mode");
RNA_def_property_enum_items(prop, prop_hexagon_value_items);
RNA_def_property_ui_text(prop, "Value Mode", "Method for drawing hexagon shape");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "direction");
RNA_def_property_enum_items(prop, prop_hexagon_direction_items);
RNA_def_property_ui_text(prop, "Direction", "Direction of hexagon pattern");
RNA_def_property_update(prop, 0, "rna_Node_update");
prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "use_clamp", 1);
RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0..1 range");
RNA_def_property_update(prop, 0, "rna_Node_update");
}
static void def_sh_tex_magic(StructRNA *srna)
{
+1
View File
@@ -130,6 +130,7 @@ DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COM
DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models")
DefNode(ShaderNode, SH_NODE_MIX, def_sh_mix, "MIX", Mix, "Mix", "Mix values by a factor")
DefNode(ShaderNode, SH_NODE_SHADER_INFO, def_sh_shader_info, "SHADERINFO", ShaderInfo, "Shader Info", "Separate internal lighting into multiple outputs, and allow for per-node light-groups.")
DefNode(ShaderNode, SH_NODE_TEX_HEXAGON, def_sh_tex_hexagon, "TEX_HEXAGON", TexHexagon, "Hex Grid Texture", "" )
DefNode(ShaderNode, SH_NODE_SCREENSPACE_INFO, 0, "SCREENSPACEINFO", ScreenspaceInfo, "Screenspace Info", "Sample internal colour and depth buffers")
DefNode(ShaderNode, SH_NODE_SET_DEPTH, 0, "SET_DEPTH", SetDepth, "Set Depth", "Pixel depth offset")
DefNode(ShaderNode, SH_NODE_CURVATURE, 0, "CURVATURE", Curvature, "Curvature", "" )
@@ -48,6 +48,7 @@ set(SRC
nodes/node_shader_camera.cc
nodes/node_shader_clamp.cc
nodes/node_shader_color_ramp.cc
nodes/node_shader_tex_hexagon.cc
nodes/node_shader_common.cc
nodes/node_shader_curvature.cc
nodes/node_shader_curves.cc
@@ -39,6 +39,7 @@ void register_shader_nodes()
register_node_type_sh_combhsv();
register_node_type_sh_combrgb();
register_node_type_sh_combxyz();
register_node_type_sh_tex_hexagon();
register_node_type_sh_curvature();
register_node_type_sh_curve_float();
register_node_type_sh_curve_rgb();
@@ -35,6 +35,7 @@ void register_node_type_sh_combcolor();
void register_node_type_sh_combhsv();
void register_node_type_sh_combrgb();
void register_node_type_sh_combxyz();
void register_node_type_sh_tex_hexagon();
void register_node_type_sh_curvature();
void register_node_type_sh_curve_float();
void register_node_type_sh_curve_rgb();
@@ -0,0 +1,455 @@
/* SPDX-FileCopyrightText: 2005 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "node_shader_util.hh"
#include "node_util.hh"
#include "BKE_texture.h"
#include "BLI_hash.hh"
#include "BLI_math_vector.hh"
#include "BLI_noise.hh"
#include "NOD_multi_function.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "node_shader_util.hh"
#include "NOD_math_functions.hh"
namespace blender::nodes::node_shader_tex_hexagon_cc {
NODE_STORAGE_FUNCS(NodeTexHexagon)
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>("Vector").hide_value().implicit_field(implicit_field_inputs::position);
b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
b.add_input<decl::Float>("Size").min(0.0f).max(16.0f).default_value(1.0f);
b.add_input<decl::Float>("Radius").min(0.0f).max(1000.0f).default_value(0.0f);
b.add_input<decl::Float>("Roundness").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_output<decl::Float>("Value").no_muted_links();
b.add_output<decl::Color>("Color").no_muted_links();
b.add_output<decl::Vector>("Hex Coords").no_muted_links();
b.add_output<decl::Vector>("Position").no_muted_links();
b.add_output<decl::Vector>("Cell UV").no_muted_links();
b.add_output<decl::Vector>("Cell ID").no_muted_links();
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiLayout *col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "value_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiItemR(col, ptr, "direction", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiItemR(col, ptr, "coord_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
static void node_init(bNodeTree * /*ntree*/, bNode *node)
{
NodeTexHexagon *tex = MEM_cnew<NodeTexHexagon>(__func__);
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
BKE_texture_colormapping_default(&tex->base.color_mapping);
node->storage = tex;
}
static int node_shader_gpu_tex_hexagon(GPUMaterial *mat,
bNode *node,
bNodeExecData * /*execdata*/,
GPUNodeStack *in,
GPUNodeStack *out)
{
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexHexagon *tex = (NodeTexHexagon *)node->storage;
float coord_mode = tex->coord_mode;
float value_mode = tex->value_mode;
float direction = tex->direction;
int ret = GPU_stack_link(mat,
node,
"node_tex_hexagon",
in,
out,
GPU_constant(&coord_mode),
GPU_constant(&value_mode),
GPU_constant(&direction));
if (ret && tex->use_clamp) {
float min[3] = {0.0f, 0.0f, 0.0f};
float max[3] = {1.0f, 1.0f, 1.0f};
GPU_link(mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
}
return ret;
}
static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sockRadius = bke::node_find_socket(node, SOCK_IN, "Radius");
bNodeSocket *sockRoundness = bke::node_find_socket(node, SOCK_IN, "Roundness");
NodeTexHexagon *tex = (NodeTexHexagon *)node->storage;
bke::node_set_socket_availability(ntree, sockRadius, !ELEM(tex->value_mode, SHD_HEXAGON_VALUE_DOT));
bke::node_set_socket_availability(
ntree, sockRoundness, ELEM(tex->value_mode, SHD_HEXAGON_VALUE_SDF));
}
class HexagonFunction : public mf::MultiFunction {
private:
const int coord_mode_;
const int direction_;
const int use_clamp_;
const int value_mode_;
public:
HexagonFunction(const int coord_mode,
const int direction,
const int use_clamp,
const int value_mode)
: coord_mode_(coord_mode),
direction_(direction),
use_clamp_(use_clamp),
value_mode_(value_mode)
{
static std::array<mf::Signature, 3> signatures{
create_signature(SHD_HEXAGON_VALUE_HEX),
create_signature(SHD_HEXAGON_VALUE_SDF),
create_signature(SHD_HEXAGON_VALUE_DOT),
};
this->set_signature(&signatures[value_mode]);
}
static mf::Signature create_signature(int value_mode)
{
mf::Signature signature;
mf::SignatureBuilder builder{"Hexagon", signature};
builder.single_input<float3>("Vector");
builder.single_input<float>("Scale");
builder.single_input<float>("Size");
switch (value_mode) {
case SHD_HEXAGON_VALUE_SDF: {
builder.single_input<float>("Radius");
builder.single_input<float>("Roundness");
break;
}
case SHD_HEXAGON_VALUE_HEX: {
builder.single_input<float>("Radius");
break;
}
case SHD_HEXAGON_VALUE_DOT: {
/* Ignore. */
break;
}
}
builder.single_output<float>("Value");
builder.single_output<ColorGeometry4f>("Color");
builder.single_output<float3>("Hex Coords");
builder.single_output<float3>("Position");
builder.single_output<float3>("Cell UV");
builder.single_output<float3>("Cell ID");
return signature;
}
/* Hexagon */
#define HRATIO 1.1547005f
#define HSQRT3 1.7320508f
#define HSQRT2 1.4142136f
#define NODE_HEXAGON_DIRECTION_HORIZONTAL 0
#define NODE_HEXAGON_DIRECTION_VERTICAL 1
#define NODE_HEXAGON_DIRECTION_HORIZONTAL_TILED 2
#define NODE_HEXAGON_DIRECTION_VERTICAL_TILED 3
/*
* SDF Functions based on:
* -
* https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
*/
static float sdf_dimension(float w, float *r)
{
float roundness = *r;
float sw = math::sign(w);
w = math::abs(w);
roundness = math::interpolate(0.0f, w, math::clamp(roundness, 0.0f, 1.0f));
const float dimension = math::max(w - roundness, 0.0f);
*r = roundness * 0.5f;
return dimension * sw;
}
static float hex_value_sdf(const float3 pos, float r, float rd)
{
float2 p = float2(pos.x, pos.y);
r = sdf_dimension(r, &rd);
const float3 k = float3(HSQRT3 * -0.5f, 0.5f, HRATIO * 0.5f);
p = math::abs(p);
float2 kxy = float2(k.x, k.y);
p = p - (2.0f * math::min(math::dot(kxy, p), 0.0f) * kxy);
p = p - float2(math::clamp(p.x, -k.z * r, k.z * r), r);
return math::length(p) * math::sign(p.y) - rd * 2.0f;
}
static float hex_value(const float3 hp, const float radius)
{
float3 fac = float3(math::abs(hp.x - hp.y), math::abs(hp.y - hp.z), math::abs(hp.z - hp.x));
float f = math::max(fac.x, math::max(fac.y, fac.z));
return (radius == 0.0f) ? f : math::interpolate(f, math::length(fac) / HSQRT2, radius);
}
static float3 xy_to_hex(const float3 xy, const float ratio)
{
float3 p = xy;
p.x *= ratio;
p.z = -0.5f * p.x - p.y;
p.y = -0.5f * p.x + p.y;
return p;
}
static float hexagon(float3 p,
const float scale,
const float size,
const float radius,
const float roundness,
const int coord_mode,
const int value_mode,
const int direction,
float3 *hex_coords,
float3 *grid_position,
float3 *cell_coords,
float3 *cell_id,
bool calc_value)
{
const float ratio = (direction == NODE_HEXAGON_DIRECTION_HORIZONTAL_TILED ||
direction == NODE_HEXAGON_DIRECTION_VERTICAL_TILED) ?
1.0f :
HRATIO;
if (direction == NODE_HEXAGON_DIRECTION_VERTICAL ||
direction == NODE_HEXAGON_DIRECTION_VERTICAL_TILED)
{
p = float3(p.y, p.x, p.y);
}
p = xy_to_hex(p * scale, ratio);
*hex_coords = p;
float3 ip = math::floor(p + 0.5f);
const float s = ip.x + ip.y + ip.z;
float3 abs_d = float3(0.0f);
if (s != 0.0f) {
abs_d = math::abs(ip - p);
if (abs_d.x >= abs_d.y && abs_d.x >= abs_d.z) {
ip.x -= s;
}
else if (abs_d.y >= abs_d.x && abs_d.y >= abs_d.z) {
ip.y -= s;
}
else {
ip.z -= s;
}
}
float3 hp = p - ip;
hp *= (size != 0.0f) ? 1.0f / size : 0.0f;
const float3 xy_coords = float3(hp.x * HSQRT3, hp.y - hp.z, 0.0f);
if (coord_mode == SHD_HEXAGON_COORDS_HEX) {
*cell_coords = hp;
*cell_id = ip;
}
else {
*cell_coords = xy_coords;
*cell_id = float3(
ip.x / ratio, (ip.y - ip.z + (1.0f - safe_floored_modf(ip.x, 2.0f))) / 2.0f, 0.0f);
}
if (direction == NODE_HEXAGON_DIRECTION_VERTICAL ||
direction == NODE_HEXAGON_DIRECTION_VERTICAL_TILED)
{
hp = float3(hp.y, hp.x, hp.z);
*cell_coords = float3(cell_coords->y, cell_coords->x, cell_coords->z);
*cell_id = float3(cell_id->y, cell_id->x, cell_id->z);
}
*grid_position = math::safe_divide(*cell_id, float3(scale));
/* Calc value. */
if (!calc_value) {
return 0.0f;
}
if (value_mode == SHD_HEXAGON_VALUE_DOT) {
return math::length(hp);
}
else if (value_mode == SHD_HEXAGON_VALUE_SDF) {
return hex_value_sdf(xy_coords, radius, roundness);
}
else { /* SHD_HEXAGON_VALUE_HEX */
return hex_value(hp, radius);
}
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
auto get_float_input = [&](int param_index, StringRef name) -> const VArray<float> {
return params.readonly_single_input<float>(param_index, name);
};
auto get_float3_input = [&](int param_index, StringRef name) -> const VArray<float3> {
return params.readonly_single_input<float3>(param_index, name);
};
auto get_r_value = [&](int param_index) -> MutableSpan<float> {
return params.uninitialized_single_output_if_required<float>(param_index, "Value");
};
auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> {
return params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index, "Color");
};
auto get_r_float3 = [&](int param_index, StringRef name) -> MutableSpan<float3> {
return params.uninitialized_single_output<float3>(param_index, name);
};
int param = 0;
const VArray<float3> &vector = get_float3_input(param++, "Vector");
const VArray<float> &scale = get_float_input(param++, "Scale");
const VArray<float> &size = get_float_input(param++, "Size");
switch (value_mode_) {
case SHD_HEXAGON_VALUE_SDF: {
const VArray<float> &radius = get_float_input(param++, "Radius");
const VArray<float> &roundness = get_float_input(param++, "Roundness");
MutableSpan<float> r_value = get_r_value(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_coords = get_r_float3(param++, "Hex Coords");
MutableSpan<float3> r_position = get_r_float3(param++, "Position");
MutableSpan<float3> r_cell_uv = get_r_float3(param++, "Cell UV");
MutableSpan<float3> r_cell_id = get_r_float3(param++, "Cell ID");
const bool calc_value = !r_value.is_empty();
const bool calc_color = !r_color.is_empty();
mask.foreach_index([&](const int64_t i) {
float3 cell_id;
const float value = hexagon(vector[i],
scale[i],
size[i],
radius[i],
roundness[i],
coord_mode_,
value_mode_,
direction_,
&r_coords[i],
&r_position[i],
&r_cell_uv[i],
&cell_id,
calc_value);
r_cell_id[i] = cell_id;
if (calc_value) {
r_value[i] = use_clamp_ ? math::clamp(value, 0.0f, 1.0f) : value;
}
if (calc_color) {
const float3 color = noise::hash_float_to_float3(cell_id);
r_color[i] = ColorGeometry4f(color[0], color[1], color[2], 1.0f);
}
});
break;
}
case SHD_HEXAGON_VALUE_HEX: {
const VArray<float> &radius = get_float_input(param++, "Radius");
MutableSpan<float> r_value = get_r_value(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_coords = get_r_float3(param++, "Hex Coords");
MutableSpan<float3> r_position = get_r_float3(param++, "Position");
MutableSpan<float3> r_cell_uv = get_r_float3(param++, "Cell UV");
MutableSpan<float3> r_cell_id = get_r_float3(param++, "Cell ID");
const bool calc_value = !r_value.is_empty();
const bool calc_color = !r_color.is_empty();
mask.foreach_index([&](const int64_t i) {
float3 cell_id;
const float value = hexagon(vector[i],
scale[i],
size[i],
radius[i],
0.0f,
coord_mode_,
value_mode_,
direction_,
&r_coords[i],
&r_position[i],
&r_cell_uv[i],
&cell_id,
calc_value);
r_cell_id[i] = cell_id;
if (calc_value) {
r_value[i] = use_clamp_ ? math::clamp(value, 0.0f, 1.0f) : value;
}
if (calc_color) {
const float3 color = noise::hash_float_to_float3(cell_id);
r_color[i] = ColorGeometry4f(color[0], color[1], color[2], 1.0f);
}
});
break;
}
case SHD_HEXAGON_VALUE_DOT: {
MutableSpan<float> r_value = get_r_value(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_coords = get_r_float3(param++, "Hex Coords");
MutableSpan<float3> r_position = get_r_float3(param++, "Position");
MutableSpan<float3> r_cell_uv = get_r_float3(param++, "Cell UV");
MutableSpan<float3> r_cell_id = get_r_float3(param++, "Cell ID");
const bool calc_value = !r_value.is_empty();
const bool calc_color = !r_color.is_empty();
mask.foreach_index([&](const int64_t i) {
float3 cell_id;
const float value = hexagon(vector[i],
scale[i],
size[i],
0.0f,
0.0f,
coord_mode_,
value_mode_,
direction_,
&r_coords[i],
&r_position[i],
&r_cell_uv[i],
&cell_id,
calc_value);
r_cell_id[i] = cell_id;
if (calc_value) {
r_value[i] = use_clamp_ ? math::clamp(value, 0.0f, 1.0f) : value;
}
if (calc_color) {
const float3 color = noise::hash_float_to_float3(cell_id);
r_color[i] = ColorGeometry4f(color[0], color[1], color[2], 1.0f);
}
});
break;
}
}
}
};
static void build_multi_function(NodeMultiFunctionBuilder &builder)
{
const bNode &node = builder.node();
NodeTexHexagon *tex = (NodeTexHexagon *)node.storage;
builder.construct_and_set_matching_fn<HexagonFunction>(
tex->coord_mode, tex->direction, tex->use_clamp, tex->value_mode);
}
} // namespace blender::nodes::node_shader_tex_hexagon_cc
void register_node_type_sh_tex_hexagon()
{
namespace file_ns = blender::nodes::node_shader_tex_hexagon_cc;
static blender::bke::bNodeType ntype;
sh_fn_node_type_base(&ntype, SH_NODE_TEX_HEXAGON, "Hex Grid Texture", NODE_CLASS_TEXTURE);
ntype.declare = file_ns::node_declare;
ntype.draw_buttons = file_ns::node_layout;
ntype.initfunc = file_ns::node_init;
ntype.gpu_fn = file_ns::node_shader_gpu_tex_hexagon;
ntype.updatefunc = file_ns::node_update;
node_type_storage(
&ntype, "NodeTexHexagon", node_free_standard_storage, node_copy_standard_storage);
ntype.build_multi_function = file_ns::build_multi_function;
node_register_type(&ntype);
}