diff options
author | Lukas Tönne <lukas.toenne@gmail.com> | 2021-06-17 10:59:51 +0300 |
---|---|---|
committer | Lukas Tönne <lukas.toenne@gmail.com> | 2021-06-17 10:59:51 +0300 |
commit | bd5b2501706d78667f1f9c28465ede64ce04cf92 (patch) | |
tree | e39e00443d29fbd265411031929832429f6791eb | |
parent | bdf792a42190402ed945007d80d65d3a01e0f41e (diff) |
Move AttributeInterpolator util class to bke.
Moved the utility class to BKE_mesh_sample.
Renamed it to MeshAttributeInterpolator to make clear that it works
on mesh attributes only.
7 files changed, 175 insertions, 233 deletions
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index f504650e349..c40d2e947e3 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -28,6 +28,11 @@ struct Mesh; +namespace blender::bke { +struct ReadAttributeLookup; +class OutputAttribute; +} // namespace blender::bke + namespace blender::bke::mesh_surface_sample { using fn::CPPType; @@ -35,6 +40,8 @@ using fn::GMutableSpan; using fn::GSpan; using fn::GVArray; +Span<MLoopTri> get_mesh_looptris(const Mesh &mesh); + void sample_point_attribute(const Mesh &mesh, Span<int> looptri_indices, Span<float3> bary_coords, @@ -52,4 +59,39 @@ void sample_face_attribute(const Mesh &mesh, const GVArray &data_in, GMutableSpan data_out); +enum class eAttributeMapMode { + INTERPOLATED, + NEAREST, +}; + +/** + * A utility class that performs attribute interpolation from a source mesh. + * + * The interpolator is only valid as long as the mesh is valid. + * Barycentric weights are needed when interpolating point or corner domain attributes, + * these are computed lazily when needed and re-used. + */ +class MeshAttributeInterpolator { + private: + const Mesh *mesh_; + const Span<float3> positions_; + const Span<int> looptri_indices_; + + Array<float3> bary_coords_; + Array<float3> nearest_weights_; + + public: + MeshAttributeInterpolator(const Mesh *mesh, + const Span<float3> positions, + const Span<int> looptri_indices); + + void sample_attribute(const ReadAttributeLookup &src_attribute, + OutputAttribute &dst_attribute, + eAttributeMapMode mode); + + protected: + Span<float3> ensure_barycentric_coords(); + Span<float3> ensure_nearest_weights(); +}; + } // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index 91c9951ae89..f52098e9b40 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -14,6 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" @@ -23,7 +24,7 @@ namespace blender::bke::mesh_surface_sample { -static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) +Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) { /* This only updates a cache and can be considered to be logically const. */ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); @@ -155,4 +156,124 @@ void sample_face_attribute(const Mesh &mesh, }); } +MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh, + const Span<float3> positions, + const Span<int> looptri_indices) + : mesh_(mesh), positions_(positions), looptri_indices_(looptri_indices) +{ + BLI_assert(positions.size() == looptri_indices.size()); +} + +Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords() +{ + if (!bary_coords_.is_empty()) { + BLI_assert(bary_coords_.size() == positions_.size()); + return bary_coords_; + } + bary_coords_.reinitialize(positions_.size()); + + Span<MLoopTri> looptris = get_mesh_looptris(*mesh_); + + for (const int i : bary_coords_.index_range()) { + const int looptri_index = looptri_indices_[i]; + const MLoopTri &looptri = looptris[looptri_index]; + + const int v0_index = mesh_->mloop[looptri.tri[0]].v; + const int v1_index = mesh_->mloop[looptri.tri[1]].v; + const int v2_index = mesh_->mloop[looptri.tri[2]].v; + + interp_weights_tri_v3(bary_coords_[i], + mesh_->mvert[v0_index].co, + mesh_->mvert[v1_index].co, + mesh_->mvert[v2_index].co, + positions_[i]); + } + return bary_coords_; +} + +Span<float3> MeshAttributeInterpolator::ensure_nearest_weights() +{ + if (!nearest_weights_.is_empty()) { + BLI_assert(nearest_weights_.size() == positions_.size()); + return nearest_weights_; + } + nearest_weights_.reinitialize(positions_.size()); + + Span<MLoopTri> looptris = get_mesh_looptris(*mesh_); + + for (const int i : nearest_weights_.index_range()) { + const int looptri_index = looptri_indices_[i]; + const MLoopTri &looptri = looptris[looptri_index]; + + const int v0_index = mesh_->mloop[looptri.tri[0]].v; + const int v1_index = mesh_->mloop[looptri.tri[1]].v; + const int v2_index = mesh_->mloop[looptri.tri[2]].v; + + const float d0 = len_squared_v3v3(positions_[i], mesh_->mvert[v0_index].co); + const float d1 = len_squared_v3v3(positions_[i], mesh_->mvert[v1_index].co); + const float d2 = len_squared_v3v3(positions_[i], mesh_->mvert[v2_index].co); + + nearest_weights_[i] = MIN3_PAIR(d0, d1, d2, float3(1, 0, 0), float3(0, 1, 0), float3(0, 0, 1)); + } + return nearest_weights_; +} + +void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, + OutputAttribute &dst_attribute, + eAttributeMapMode mode) +{ + if (looptri_indices_.is_empty()) { + return; + } + + if (!src_attribute || !dst_attribute) { + return; + } + const GVArray &src_varray = *src_attribute.varray; + GMutableSpan dst_span = dst_attribute.as_span(); + if (src_varray.is_empty() || dst_span.is_empty()) { + return; + } + + /* Compute barycentric coordinates only when they are needed. */ + Span<float3> weights; + if (ELEM(src_attribute.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + switch (mode) { + case eAttributeMapMode::INTERPOLATED: + weights = ensure_barycentric_coords(); + break; + case eAttributeMapMode::NEAREST: + weights = ensure_nearest_weights(); + break; + } + } + + /* Interpolate the source attributes on the surface. */ + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: { + sample_point_attribute( + *mesh_, looptri_indices_, weights, src_varray, dst_span); + break; + } + case ATTR_DOMAIN_FACE: { + sample_face_attribute( + *mesh_, looptri_indices_, src_varray, dst_span); + break; + } + case ATTR_DOMAIN_CORNER: { + sample_corner_attribute( + *mesh_, looptri_indices_, weights, src_varray, dst_span); + break; + } + case ATTR_DOMAIN_EDGE: { + /* Not yet supported. */ + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + } // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 0674ad767b6..bc2349e8834 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -204,7 +204,6 @@ set(SRC geometry/node_geometry_exec.cc geometry/node_geometry_tree.cc geometry/node_geometry_util.cc - geometry/node_geometry_util_interp.cc shader/nodes/node_shader_add_shader.c shader/nodes/node_shader_ambient_occlusion.c @@ -341,7 +340,6 @@ set(SRC function/node_function_util.hh shader/node_shader_util.h geometry/node_geometry_util.hh - geometry/node_geometry_util_interp.hh texture/node_texture_util.h NOD_common.h diff --git a/source/blender/nodes/geometry/node_geometry_util_interp.cc b/source/blender/nodes/geometry/node_geometry_util_interp.cc deleted file mode 100644 index 879442afb7a..00000000000 --- a/source/blender/nodes/geometry/node_geometry_util_interp.cc +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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 "node_geometry_util_interp.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_sample.hh" - -#include "NOD_geometry_exec.hh" - -namespace blender::nodes { - -Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) -{ - /* This only updates a cache and can be considered to be logically const. */ - const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); - const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); - return {looptris, looptris_len}; -} - -AttributeInterpolator::AttributeInterpolator(const Mesh *mesh, - const Span<float3> positions, - const Span<int> looptri_indices) - : mesh_(mesh), positions_(positions), looptri_indices_(looptri_indices) -{ - BLI_assert(positions.size() == looptri_indices.size()); -} - -Span<float3> AttributeInterpolator::ensure_barycentric_coords() -{ - if (!bary_coords_.is_empty()) { - BLI_assert(bary_coords_.size() == positions_.size()); - return bary_coords_; - } - bary_coords_.reinitialize(positions_.size()); - - Span<MLoopTri> looptris = get_mesh_looptris(*mesh_); - - for (const int i : bary_coords_.index_range()) { - const int looptri_index = looptri_indices_[i]; - const MLoopTri &looptri = looptris[looptri_index]; - - const int v0_index = mesh_->mloop[looptri.tri[0]].v; - const int v1_index = mesh_->mloop[looptri.tri[1]].v; - const int v2_index = mesh_->mloop[looptri.tri[2]].v; - - interp_weights_tri_v3(bary_coords_[i], - mesh_->mvert[v0_index].co, - mesh_->mvert[v1_index].co, - mesh_->mvert[v2_index].co, - positions_[i]); - } - return bary_coords_; -} - -Span<float3> AttributeInterpolator::ensure_nearest_weights() -{ - if (!nearest_weights_.is_empty()) { - BLI_assert(nearest_weights_.size() == positions_.size()); - return nearest_weights_; - } - nearest_weights_.reinitialize(positions_.size()); - - Span<MLoopTri> looptris = get_mesh_looptris(*mesh_); - - for (const int i : nearest_weights_.index_range()) { - const int looptri_index = looptri_indices_[i]; - const MLoopTri &looptri = looptris[looptri_index]; - - const int v0_index = mesh_->mloop[looptri.tri[0]].v; - const int v1_index = mesh_->mloop[looptri.tri[1]].v; - const int v2_index = mesh_->mloop[looptri.tri[2]].v; - - const float d0 = len_squared_v3v3(positions_[i], mesh_->mvert[v0_index].co); - const float d1 = len_squared_v3v3(positions_[i], mesh_->mvert[v1_index].co); - const float d2 = len_squared_v3v3(positions_[i], mesh_->mvert[v2_index].co); - - nearest_weights_[i] = MIN3_PAIR(d0, d1, d2, float3(1, 0, 0), float3(0, 1, 0), float3(0, 0, 1)); - } - return nearest_weights_; -} - -void AttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, - OutputAttribute &dst_attribute, - eAttributeMapMode mode) -{ - if (looptri_indices_.is_empty()) { - return; - } - - if (!src_attribute || !dst_attribute) { - return; - } - const GVArray &src_varray = *src_attribute.varray; - GMutableSpan dst_span = dst_attribute.as_span(); - if (src_varray.is_empty() || dst_span.is_empty()) { - return; - } - - /* Compute barycentric coordinates only when they are needed. */ - Span<float3> weights; - if (ELEM(src_attribute.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { - switch (mode) { - case eAttributeMapMode::INTERPOLATED: - weights = ensure_barycentric_coords(); - break; - case eAttributeMapMode::NEAREST: - weights = ensure_nearest_weights(); - break; - } - } - - /* Interpolate the source attributes on the surface. */ - switch (src_attribute.domain) { - case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - *mesh_, looptri_indices_, weights, src_varray, dst_span); - break; - } - case ATTR_DOMAIN_FACE: { - bke::mesh_surface_sample::sample_face_attribute( - *mesh_, looptri_indices_, src_varray, dst_span); - break; - } - case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - *mesh_, looptri_indices_, weights, src_varray, dst_span); - break; - } - case ATTR_DOMAIN_EDGE: { - /* Not yet supported. */ - break; - } - default: { - BLI_assert_unreachable(); - break; - } - } -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/geometry/node_geometry_util_interp.hh b/source/blender/nodes/geometry/node_geometry_util_interp.hh deleted file mode 100644 index f547a010906..00000000000 --- a/source/blender/nodes/geometry/node_geometry_util_interp.hh +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 "BKE_attribute_access.hh" - -struct Mesh; -struct MLoopTri; - -namespace blender::nodes { - -Span<MLoopTri> get_mesh_looptris(const Mesh &mesh); - -enum class eAttributeMapMode { - INTERPOLATED, - NEAREST, -}; - -/** - * A utility class that performs attribute interpolation from a source mesh. - * - * The interpolator is only valid as long as the mesh is valid. - * Barycentric weights are needed when interpolating point or corner domain attributes, - * these are computed lazily when needed and re-used. - */ -class AttributeInterpolator { - private: - const Mesh *mesh_; - const Span<float3> positions_; - const Span<int> looptri_indices_; - - Array<float3> bary_coords_; - Array<float3> nearest_weights_; - - public: - AttributeInterpolator(const Mesh *mesh, - const Span<float3> positions, - const Span<int> looptri_indices); - - void sample_attribute(const blender::bke::ReadAttributeLookup &src_attribute, - blender::bke::OutputAttribute &dst_attribute, - eAttributeMapMode mode); - - protected: - Span<float3> ensure_barycentric_coords(); - Span<float3> ensure_nearest_weights(); -}; - -} // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc index 78fb3805cf0..d1114713672 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -28,7 +28,6 @@ #include "UI_resources.h" #include "node_geometry_util.hh" -#include "node_geometry_util_interp.hh" static bNodeSocketTemplate geo_node_attribute_transfer_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, @@ -205,7 +204,7 @@ static void get_closest_mesh_polygons(const Mesh &mesh, Array<int> looptri_indices(positions.size()); get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions); - Span<MLoopTri> looptris = get_mesh_looptris(mesh); + Span<MLoopTri> looptris = bke::mesh_surface_sample::get_mesh_looptris(mesh); for (const int i : positions.index_range()) { const MLoopTri &looptri = looptris[looptri_indices[i]]; r_poly_indices[i] = looptri.poly; @@ -288,8 +287,9 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_ Array<float3> positions(tot_samples); get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); - AttributeInterpolator interp(mesh, positions, looptri_indices); - interp.sample_attribute(src_attribute, dst_attribute, eAttributeMapMode::INTERPOLATED); + bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices); + interp.sample_attribute( + src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED); dst_attribute.save(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 5adf137067c..f41dd5a9ca3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -17,12 +17,12 @@ #include "DNA_mesh_types.h" #include "BKE_bvhutils.h" +#include "BKE_mesh_sample.hh" #include "UI_interface.h" #include "UI_resources.h" #include "node_geometry_util.hh" -#include "node_geometry_util_interp.hh" static bNodeSocketTemplate geo_node_raycast_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, @@ -153,14 +153,14 @@ static void raycast_to_mesh(const Mesh *mesh, } } -static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode) +static bke::mesh_surface_sample::eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode) { switch (map_mode) { case GEO_NODE_RAYCAST_INTERPOLATED: - return eAttributeMapMode::INTERPOLATED; + return bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED; default: case GEO_NODE_RAYCAST_NEAREST: - return eAttributeMapMode::NEAREST; + return bke::mesh_surface_sample::eAttributeMapMode::NEAREST; } } @@ -189,7 +189,8 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, } const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; - eAttributeMapMode map_mode = get_map_mode((GeometryNodeRaycastMapMode)storage.mapping); + bke::mesh_surface_sample::eAttributeMapMode map_mode = get_map_mode( + (GeometryNodeRaycastMapMode)storage.mapping); const AttributeDomain result_domain = ATTR_DOMAIN_POINT; GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>( @@ -246,7 +247,7 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, hit_distance_attribute.save(); /* Custom interpolated attributes */ - AttributeInterpolator interp(src_mesh, hit_positions, hit_indices); + bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices); for (int i = 0; i < hit_attribute_names.size(); ++i) { const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data( hit_attribute_names[i]); |