Geometry Nodes: new Image Texture node

This adds a new image texture node for geometry nodes. It does not
reuse the same node that is used in shading, because we want to be
able to expose the image and frame as sockets.

There is a known update issue when a movie or image sequence is
used. That will be fixed separately (also see D12957).

Currently, the image socket is just a pointer to an Image ID data block.
This can contain single images but also movies and image sequences.
In the future, the definition of an image socket can be expanded to
include images that are generated from scratch in the node tree.
For more details read the discussion in D12827.

Some of the code is a direct port from cycles and should be cleaned
up a bit in the future. For example `image_cubic_texture_lookup`.

For still images, the frame input is ignored. Otherwise, the frame
has to be in a valid range for the node to work. In the future we
may add e.g. automatic looping functionality.

Differential Revision: https://developer.blender.org/D12827
This commit is contained in:
Jacques Lucke 2021-10-25 13:02:43 +02:00
parent 2b91445ddd
commit 039094c1ec
Notes: blender-bot 2023-02-13 17:33:16 +01:00
Referenced by issue #91847, Image Texture node
11 changed files with 530 additions and 8 deletions

View File

@ -735,6 +735,7 @@ geometry_node_categories = [
NodeItem("ShaderNodeTexVoronoi"),
NodeItem("ShaderNodeTexWave"),
NodeItem("ShaderNodeTexWhiteNoise"),
NodeItem("GeometryNodeImageTexture"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),

View File

@ -1547,6 +1547,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_RAYCAST 1128
#define GEO_NODE_CURVE_TO_POINTS 1130
#define GEO_NODE_INSTANCES_TO_POINTS 1131
#define GEO_NODE_IMAGE_TEXTURE 1132
/** \} */

View File

@ -5800,6 +5800,7 @@ static void registerGeometryNodes()
register_node_type_geo_delete_geometry();
register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_edge_split();
register_node_type_geo_image_texture();
register_node_type_geo_input_curve_handles();
register_node_type_geo_input_curve_tilt();
register_node_type_geo_input_index();

View File

@ -3619,7 +3619,39 @@ static void std_node_socket_draw(
break;
}
case SOCK_IMAGE: {
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id;
if (node_tree->type == NTREE_GEOMETRY) {
if (text[0] == '\0') {
uiTemplateID(layout,
C,
ptr,
"default_value",
"image.new",
"image.open",
nullptr,
0,
ICON_NONE,
nullptr);
}
else {
/* 0.3 split ratio is inconsistent, but use it here because the "New" button is large. */
uiLayout *row = uiLayoutSplit(layout, 0.3f, false);
uiItemL(row, text, 0);
uiTemplateID(row,
C,
ptr,
"default_value",
"image.new",
"image.open",
nullptr,
0,
ICON_NONE,
nullptr);
}
}
else {
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
}
break;
}
case SOCK_COLLECTION: {

View File

@ -758,7 +758,7 @@ static bool node_add_file_poll(bContext *C)
{
const SpaceNode *snode = CTX_wm_space_node(C);
return ED_operator_node_editable(C) &&
ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT);
ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT, NTREE_GEOMETRY);
}
static int node_add_file_exec(bContext *C, wmOperator *op)
@ -784,6 +784,9 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
case NTREE_COMPOSIT:
type = CMP_NODE_IMAGE;
break;
case NTREE_GEOMETRY:
type = GEO_NODE_IMAGE_TEXTURE;
break;
default:
return OPERATOR_CANCELLED;
}
@ -797,7 +800,14 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
node->id = (ID *)ima;
if (type == GEO_NODE_IMAGE_TEXTURE) {
bNodeSocket *image_socket = (bNodeSocket *)node->inputs.first;
bNodeSocketValueImage *socket_value = (bNodeSocketValueImage *)image_socket->default_value;
socket_value->value = ima;
}
else {
node->id = (ID *)ima;
}
/* When adding new image file via drag-drop we need to load imbuf in order
* to get proper image source.

View File

@ -1581,6 +1581,11 @@ typedef struct NodeGeometrySeparateGeometry {
int8_t domain;
} NodeGeometrySeparateGeometry;
typedef struct NodeGeometryImageTexture {
int interpolation;
int extension;
} NodeGeometryImageTexture;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1

View File

@ -5022,10 +5022,8 @@ static void def_fn_input_bool(StructRNA *srna)
prop = RNA_def_property(srna, "boolean", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "boolean", 1);
RNA_def_property_ui_text(
prop, "Boolean", "Input value used for unconnected socket");
RNA_def_property_ui_text(prop, "Boolean", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_fn_input_int(StructRNA *srna)
@ -5037,8 +5035,7 @@ static void def_fn_input_int(StructRNA *srna)
prop = RNA_def_property(srna, "integer", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "integer");
RNA_def_property_int_default(prop, 1);
RNA_def_property_ui_text(
prop, "Integer", "Input value used for unconnected socket");
RNA_def_property_ui_text(prop, "Integer", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
@ -5416,6 +5413,50 @@ static void def_sh_tex_image(StructRNA *srna)
RNA_def_property_update(prop, 0, "rna_Node_update");
}
static void def_geo_image_texture(StructRNA *srna)
{
static const EnumPropertyItem fn_tex_prop_interpolation_items[] = {
{SHD_INTERP_LINEAR, "Linear", 0, "Linear", "Linear interpolation"},
{SHD_INTERP_CLOSEST, "Closest", 0, "Closest", "No interpolation (sample closest texel)"},
{SHD_INTERP_CUBIC, "Cubic", 0, "Cubic", "Cubic interpolation"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem prop_image_extension[] = {
{SHD_IMAGE_EXTENSION_REPEAT,
"REPEAT",
0,
"Repeat",
"Cause the image to repeat horizontally and vertically"},
{SHD_IMAGE_EXTENSION_EXTEND,
"EXTEND",
0,
"Extend",
"Extend by repeating edge pixels of the image"},
{SHD_IMAGE_EXTENSION_CLIP,
"CLIP",
0,
"Clip",
"Clip to image size and set exterior pixels as transparent"},
{0, NULL, 0, NULL, NULL},
};
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryImageTexture", "storage");
prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, fn_tex_prop_interpolation_items);
RNA_def_property_ui_text(prop, "Interpolation", "Method for smoothing values between pixels");
RNA_def_property_update(prop, 0, "rna_Node_update");
prop = RNA_def_property(srna, "extension", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_image_extension);
RNA_def_property_ui_text(
prop, "Extension", "How the image is extrapolated past its original bounds");
RNA_def_property_update(prop, 0, "rna_Node_update");
}
static void def_sh_tex_gradient(StructRNA *srna)
{
static const EnumPropertyItem prop_gradient_type[] = {

View File

@ -226,6 +226,7 @@ set(SRC
geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_distribute_points_on_faces.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_image_texture.cc
geometry/nodes/node_geo_input_curve_handles.cc
geometry/nodes/node_geo_input_curve_tilt.cc
geometry/nodes/node_geo_input_index.cc

View File

@ -94,6 +94,7 @@ void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_image_texture(void);
void register_node_type_geo_input_curve_handles(void);
void register_node_type_geo_input_curve_tilt(void);
void register_node_type_geo_input_index(void);

View File

@ -355,6 +355,7 @@ DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE
DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "")
DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "")
DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")

View File

@ -0,0 +1,428 @@
/*
* 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.
*
* The Original Code is Copyright (C) 2005 Blender Foundation.
* All rights reserved.
*/
#include "node_geometry_util.hh"
#include "BKE_image.h"
#include "BLI_float4.hh"
#include "BLI_threads.h"
#include "BLI_timeit.hh"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes {
static void geo_node_image_texture_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Image>("Image").hide_label();
b.add_input<decl::Vector>("Vector").implicit_field().description(
"Texture coordinates from 0 to 1");
b.add_input<decl::Int>("Frame").min(0).max(MAXFRAMEF);
b.add_output<decl::Color>("Color").no_muted_links().dependent_field();
b.add_output<decl::Float>("Alpha").no_muted_links().dependent_field();
}
static void geo_node_image_texture_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
static void geo_node_image_texture_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryImageTexture *tex = (NodeGeometryImageTexture *)MEM_callocN(
sizeof(NodeGeometryImageTexture), __func__);
node->storage = tex;
}
class ImageFieldsFunction : public fn::MultiFunction {
private:
const int interpolation_;
const int extension_;
Image &image_;
ImageUser image_user_;
void *image_lock_;
ImBuf *image_buffer_;
public:
ImageFieldsFunction(const int interpolation,
const int extension,
Image &image,
ImageUser image_user)
: interpolation_(interpolation),
extension_(extension),
image_(image),
image_user_(image_user)
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_);
if (image_buffer_ == nullptr) {
throw std::runtime_error("cannot aquire image buffer");
}
if (image_buffer_->rect_float == nullptr) {
BLI_thread_lock(LOCK_IMAGE);
if (!image_buffer_->rect_float) {
IMB_float_from_rect(image_buffer_);
}
BLI_thread_unlock(LOCK_IMAGE);
}
if (image_buffer_->rect_float == nullptr) {
BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
throw std::runtime_error("cannot get float buffer");
}
}
~ImageFieldsFunction() override
{
BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
}
static fn::MFSignature create_signature()
{
fn::MFSignatureBuilder signature{"ImageFunction"};
signature.single_input<float3>("Vector");
signature.single_output<ColorGeometry4f>("Color");
signature.single_output<float>("Alpha");
return signature.build();
}
static int wrap_periodic(int x, const int width)
{
x %= width;
if (x < 0) {
x += width;
}
return x;
}
static int wrap_clamp(const int x, const int width)
{
return std::clamp(x, 0, width - 1);
}
static float4 image_pixel_lookup(const ImBuf *ibuf, const int px, const int py)
{
if (px < 0 || py < 0 || px >= ibuf->x || py >= ibuf->y) {
return float4(0.0f, 0.0f, 0.0f, 0.0f);
}
return ((const float4 *)ibuf->rect_float)[px + py * ibuf->x];
}
static float frac(const float x, int *ix)
{
const int i = (int)x - ((x < 0.0f) ? 1 : 0);
*ix = i;
return x - (float)i;
}
static float4 image_cubic_texture_lookup(const ImBuf *ibuf,
const float px,
const float py,
const int extension)
{
const int width = ibuf->x;
const int height = ibuf->y;
int pix, piy, nix, niy;
const float tx = frac(px * (float)width - 0.5f, &pix);
const float ty = frac(py * (float)height - 0.5f, &piy);
int ppix, ppiy, nnix, nniy;
switch (extension) {
case SHD_IMAGE_EXTENSION_REPEAT: {
pix = wrap_periodic(pix, width);
piy = wrap_periodic(piy, height);
ppix = wrap_periodic(pix - 1, width);
ppiy = wrap_periodic(piy - 1, height);
nix = wrap_periodic(pix + 1, width);
niy = wrap_periodic(piy + 1, height);
nnix = wrap_periodic(pix + 2, width);
nniy = wrap_periodic(piy + 2, height);
break;
}
case SHD_IMAGE_EXTENSION_CLIP: {
ppix = pix - 1;
ppiy = piy - 1;
nix = pix + 1;
niy = piy + 1;
nnix = pix + 2;
nniy = piy + 2;
break;
}
case SHD_IMAGE_EXTENSION_EXTEND: {
ppix = wrap_clamp(pix - 1, width);
ppiy = wrap_clamp(piy - 1, height);
nix = wrap_clamp(pix + 1, width);
niy = wrap_clamp(piy + 1, height);
nnix = wrap_clamp(pix + 2, width);
nniy = wrap_clamp(piy + 2, height);
pix = wrap_clamp(pix, width);
piy = wrap_clamp(piy, height);
break;
}
default:
return float4(0.0f, 0.0f, 0.0f, 0.0f);
}
const int xc[4] = {ppix, pix, nix, nnix};
const int yc[4] = {ppiy, piy, niy, nniy};
float u[4], v[4];
u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f);
u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f);
u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f);
u[3] = (1.0f / 6.0f) * tx * tx * tx;
v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f);
v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f);
v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f);
v[3] = (1.0f / 6.0f) * ty * ty * ty;
return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) +
u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) +
u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) +
u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) +
(v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) +
u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) +
u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) +
u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) +
(v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) +
u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) +
u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) +
u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) +
(v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) +
u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) +
u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) +
u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3]))));
}
static float4 image_linear_texture_lookup(const ImBuf *ibuf,
const float px,
const float py,
const int extension)
{
const int width = ibuf->x;
const int height = ibuf->y;
int pix, piy, nix, niy;
const float nfx = frac(px * (float)width - 0.5f, &pix);
const float nfy = frac(py * (float)height - 0.5f, &piy);
switch (extension) {
case SHD_IMAGE_EXTENSION_CLIP: {
nix = pix + 1;
niy = piy + 1;
break;
}
case SHD_IMAGE_EXTENSION_EXTEND: {
nix = wrap_clamp(pix + 1, width);
niy = wrap_clamp(piy + 1, height);
pix = wrap_clamp(pix, width);
piy = wrap_clamp(piy, height);
break;
}
default:
case SHD_IMAGE_EXTENSION_REPEAT:
pix = wrap_periodic(pix, width);
piy = wrap_periodic(piy, height);
nix = wrap_periodic(pix + 1, width);
niy = wrap_periodic(piy + 1, height);
break;
}
const float ptx = 1.0f - nfx;
const float pty = 1.0f - nfy;
return image_pixel_lookup(ibuf, pix, piy) * ptx * pty +
image_pixel_lookup(ibuf, nix, piy) * nfx * pty +
image_pixel_lookup(ibuf, pix, niy) * ptx * nfy +
image_pixel_lookup(ibuf, nix, niy) * nfx * nfy;
}
static float4 image_closest_texture_lookup(const ImBuf *ibuf,
const float px,
const float py,
const int extension)
{
const int width = ibuf->x;
const int height = ibuf->y;
int ix, iy;
const float tx = frac(px * (float)width - 0.5f, &ix);
const float ty = frac(py * (float)height - 0.5f, &iy);
switch (extension) {
case SHD_IMAGE_EXTENSION_REPEAT: {
ix = wrap_periodic(ix, width);
iy = wrap_periodic(iy, height);
return image_pixel_lookup(ibuf, ix, iy);
}
case SHD_IMAGE_EXTENSION_CLIP: {
if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) {
return float4(0.0f, 0.0f, 0.0f, 0.0f);
}
if (ix < 0 || iy < 0 || ix > width || iy > height) {
return float4(0.0f, 0.0f, 0.0f, 0.0f);
}
ATTR_FALLTHROUGH;
}
case SHD_IMAGE_EXTENSION_EXTEND: {
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
return image_pixel_lookup(ibuf, ix, iy);
}
default:
return float4(0.0f, 0.0f, 0.0f, 0.0f);
}
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "Vector");
MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>(
1, "Color");
MutableSpan<float> r_alpha = params.uninitialized_single_output_if_required<float>(2, "Alpha");
MutableSpan<float4> color_data{(float4 *)r_color.data(), r_color.size()};
/* Sample image texture. */
switch (interpolation_) {
case SHD_INTERP_LINEAR:
for (const int64_t i : mask) {
const float3 p = vectors[i];
color_data[i] = image_linear_texture_lookup(image_buffer_, p.x, p.y, extension_);
}
break;
case SHD_INTERP_CLOSEST:
for (const int64_t i : mask) {
const float3 p = vectors[i];
color_data[i] = image_closest_texture_lookup(image_buffer_, p.x, p.y, extension_);
}
break;
case SHD_INTERP_CUBIC:
case SHD_INTERP_SMART:
for (const int64_t i : mask) {
const float3 p = vectors[i];
color_data[i] = image_cubic_texture_lookup(image_buffer_, p.x, p.y, extension_);
}
break;
}
int alpha_mode = image_.alpha_mode;
if (IMB_colormanagement_space_name_is_data(image_.colorspace_settings.name)) {
alpha_mode = IMA_ALPHA_CHANNEL_PACKED;
}
switch (alpha_mode) {
case IMA_ALPHA_STRAIGHT: {
/* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */
for (int64_t i : mask) {
straight_to_premul_v4(color_data[i]);
}
break;
}
case IMA_ALPHA_PREMUL: {
/* Alpha is premultiplied already, nothing to do. */
break;
}
case IMA_ALPHA_CHANNEL_PACKED: {
/* Color and alpha channels shouldn't interact with each other, nothing to do. */
break;
}
case IMA_ALPHA_IGNORE: {
/* The image should be treated as being opaque. */
for (int64_t i : mask) {
color_data[i].w = 1.0f;
}
break;
}
}
if (!r_alpha.is_empty()) {
for (int64_t i : mask) {
r_alpha[i] = r_color[i].a;
}
}
}
};
static void geo_node_image_texture_exec(GeoNodeExecParams params)
{
auto return_default = [&]() {
params.set_output("Color", ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
params.set_output("Alpha", 1.0f);
};
Image *image = params.get_input<Image *>("Image");
if (image == nullptr) {
return return_default();
}
const bNode &node = params.node();
NodeGeometryImageTexture *data = (NodeGeometryImageTexture *)node.storage;
ImageUser image_user;
BKE_imageuser_default(&image_user);
image_user.cycl = false;
image_user.frames = INT_MAX;
image_user.sfra = 1;
image_user.framenr = BKE_image_is_animated(image) ? params.get_input<int>("Frame") : 0;
std::unique_ptr<ImageFieldsFunction> image_fn;
try {
image_fn = std::make_unique<ImageFieldsFunction>(
data->interpolation, data->extension, *image, image_user);
}
catch (const std::runtime_error &) {
return return_default();
}
Field<float3> vector_field = params.extract_input<Field<float3>>("Vector");
auto image_op = std::make_shared<FieldOperation>(
FieldOperation(std::move(image_fn), {std::move(vector_field)}));
params.set_output("Color", Field<ColorGeometry4f>(image_op, 0));
params.set_output("Alpha", Field<float>(image_op, 1));
}
} // namespace blender::nodes
void register_node_type_geo_image_texture(void)
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE, 0);
ntype.declare = blender::nodes::geo_node_image_texture_declare;
ntype.draw_buttons = blender::nodes::geo_node_image_texture_layout;
node_type_init(&ntype, blender::nodes::geo_node_image_texture_init);
node_type_storage(
&ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
ntype.geometry_node_execute = blender::nodes::geo_node_image_texture_exec;
nodeRegisterType(&ntype);
}