Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_image_texture.cc428
4 files changed, 431 insertions, 0 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 7a728b9041b..657d0d11441 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -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
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index a37dcf28757..6702443e20e 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -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);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index af14538e6cc..29cbe0d13c8 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -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", "")
diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc
new file mode 100644
index 00000000000..02f99b6cf94
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc
@@ -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);
+}