GOOENGINE: Hex Grid node
Patches originally written by Joseph and Thorn. Thank you! Implements a hex grid texture node
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user