diff options
Diffstat (limited to 'source/blender/functions/intern/multi_functions/surface_hook.cc')
-rw-r--r-- | source/blender/functions/intern/multi_functions/surface_hook.cc | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/source/blender/functions/intern/multi_functions/surface_hook.cc b/source/blender/functions/intern/multi_functions/surface_hook.cc new file mode 100644 index 00000000000..f53e522ba84 --- /dev/null +++ b/source/blender/functions/intern/multi_functions/surface_hook.cc @@ -0,0 +1,625 @@ +#include "surface_hook.h" + +#include "BKE_customdata.h" +#include "BKE_deform.h" +#include "BKE_id_data_cache.h" +#include "BKE_id_handle.h" +#include "BKE_image.h" +#include "BKE_mesh_runtime.h" +#include "BKE_surface_hook.h" + +#include "IMB_imbuf_types.h" + +#include "DNA_customdata_types.h" + +#include "BLI_array_cxx.h" +#include "BLI_color.h" +#include "BLI_float2.h" +#include "BLI_float3.h" +#include "BLI_float4x4.h" +#include "BLI_vector_adaptor.h" + +#include "sampling_util.h" +#include "util.h" + +namespace FN { + +using BKE::IDDataCache; +using BKE::IDHandleLookup; +using BKE::ImageIDHandle; +using BKE::ObjectIDHandle; +using BKE::SurfaceHook; +using BLI::Array; +using BLI::float2; +using BLI::float3; +using BLI::float4x4; +using BLI::rgba_b; +using BLI::rgba_f; +using BLI::VectorAdaptor; + +MF_ClosestSurfaceHookOnObject::MF_ClosestSurfaceHookOnObject() +{ + MFSignatureBuilder signature = this->get_builder("Closest Point on Object"); + signature.use_global_context<IDDataCache>(); + signature.use_global_context<IDHandleLookup>(); + signature.single_input<ObjectIDHandle>("Object"); + signature.single_input<float3>("Position"); + signature.single_output<SurfaceHook>("Closest Location"); +} + +static BVHTreeNearest get_nearest_point(BVHTreeFromMesh *bvhtree_data, float3 point) +{ + BVHTreeNearest nearest = {0}; + nearest.dist_sq = 10000000.0f; + nearest.index = -1; + BLI_bvhtree_find_nearest( + bvhtree_data->tree, point, &nearest, bvhtree_data->nearest_callback, (void *)bvhtree_data); + return nearest; +} + +static float3 get_barycentric_coords(Mesh *mesh, + const MLoopTri *triangles, + float3 position, + uint triangle_index) +{ + const MLoopTri &triangle = triangles[triangle_index]; + + float3 v1 = mesh->mvert[mesh->mloop[triangle.tri[0]].v].co; + float3 v2 = mesh->mvert[mesh->mloop[triangle.tri[1]].v].co; + float3 v3 = mesh->mvert[mesh->mloop[triangle.tri[2]].v].co; + + float3 weights; + interp_weights_tri_v3(weights, v1, v2, v3, position); + return weights; +} + +void MF_ClosestSurfaceHookOnObject::call(IndexMask mask, MFParams params, MFContext context) const +{ + VirtualListRef<ObjectIDHandle> object_handles = params.readonly_single_input<ObjectIDHandle>( + 0, "Object"); + VirtualListRef<float3> positions = params.readonly_single_input<float3>(1, "Position"); + MutableArrayRef<SurfaceHook> r_surface_hooks = params.uninitialized_single_output<SurfaceHook>( + 2, "Closest Location"); + + auto *id_data_cache = context.try_find_global<IDDataCache>(); + auto *id_handle_lookup = context.try_find_global<IDHandleLookup>(); + + if (id_data_cache == nullptr || id_handle_lookup == nullptr) { + r_surface_hooks.fill_indices(mask.indices(), {}); + return; + } + + group_indices_by_same_value( + mask.indices(), + object_handles, + [&](ObjectIDHandle object_handle, IndexMask indices_with_same_object) { + Object *object = id_handle_lookup->lookup(object_handle); + if (object == nullptr) { + r_surface_hooks.fill_indices(indices_with_same_object, {}); + return; + } + + BVHTreeFromMesh *bvhtree = id_data_cache->get_bvh_tree(object); + if (bvhtree == nullptr) { + r_surface_hooks.fill_indices(indices_with_same_object, {}); + return; + } + + Mesh *mesh = (Mesh *)object->data; + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(mesh); + + float4x4 global_to_local = float4x4(object->obmat).inverted__LocRotScale(); + + for (uint i : indices_with_same_object) { + float3 local_position = global_to_local.transform_position(positions[i]); + BVHTreeNearest nearest = get_nearest_point(bvhtree, local_position); + if (nearest.index == -1) { + r_surface_hooks[i] = {}; + continue; + } + + float3 bary_coords = get_barycentric_coords(mesh, triangles, nearest.co, nearest.index); + r_surface_hooks[i] = SurfaceHook(object_handle, nearest.index, bary_coords); + } + }); +} + +MF_GetPositionOnSurface::MF_GetPositionOnSurface() +{ + MFSignatureBuilder signature = this->get_builder("Get Position on Surface"); + signature.use_global_context<IDHandleLookup>(); + signature.single_input<SurfaceHook>("Surface Hook"); + signature.single_output<float3>("Position"); +} + +void MF_GetPositionOnSurface::call(IndexMask mask, MFParams params, MFContext context) const +{ + VirtualListRef<SurfaceHook> surface_hooks = params.readonly_single_input<SurfaceHook>( + 0, "Surface Hook"); + MutableArrayRef<float3> r_positions = params.uninitialized_single_output<float3>(1, "Position"); + + float3 fallback = {0, 0, 0}; + + auto *id_handle_lookup = context.try_find_global<IDHandleLookup>(); + if (id_handle_lookup == nullptr) { + r_positions.fill_indices(mask.indices(), fallback); + return; + } + + group_indices_by_same_value( + mask.indices(), + surface_hooks, + [&](SurfaceHook base_hook, IndexMask indices_on_same_surface) { + if (base_hook.type() != BKE::SurfaceHookType::MeshObject) { + r_positions.fill_indices(indices_on_same_surface, fallback); + return; + } + + Object *object = id_handle_lookup->lookup(base_hook.object_handle()); + if (object == nullptr) { + r_positions.fill_indices(indices_on_same_surface, fallback); + return; + } + + Mesh *mesh = (Mesh *)object->data; + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(mesh); + int triangle_amount = BKE_mesh_runtime_looptri_len(mesh); + + for (uint i : indices_on_same_surface) { + SurfaceHook hook = surface_hooks[i]; + + if (hook.triangle_index() >= triangle_amount) { + r_positions[i] = fallback; + continue; + } + + const MLoopTri &triangle = triangles[hook.triangle_index()]; + float3 v1 = mesh->mvert[mesh->mloop[triangle.tri[0]].v].co; + float3 v2 = mesh->mvert[mesh->mloop[triangle.tri[1]].v].co; + float3 v3 = mesh->mvert[mesh->mloop[triangle.tri[2]].v].co; + + float3 position; + interp_v3_v3v3v3(position, v1, v2, v3, hook.bary_coords()); + float4x4 local_to_world = object->obmat; + position = local_to_world.transform_position(position); + + r_positions[i] = position; + } + }, + SurfaceHook::on_same_surface); +} + +MF_GetNormalOnSurface::MF_GetNormalOnSurface() +{ + MFSignatureBuilder signature = this->get_builder("Get Normal on Surface"); + signature.use_global_context<IDHandleLookup>(); + signature.single_input<SurfaceHook>("Surface Hook"); + signature.single_output<float3>("Normal"); +} + +static float3 short_normal_to_float3(const short normal[3]) +{ + return float3( + (float)normal[0] / 32767.0f, (float)normal[1] / 32767.0f, (float)normal[2] / 32767.0f); +} + +void MF_GetNormalOnSurface::call(IndexMask mask, MFParams params, MFContext context) const +{ + VirtualListRef<SurfaceHook> surface_hooks = params.readonly_single_input<SurfaceHook>( + 0, "Surface Hook"); + MutableArrayRef<float3> r_normals = params.uninitialized_single_output<float3>(1, "Normal"); + + float3 fallback = {0, 0, 1}; + + auto *id_handle_lookup = context.try_find_global<IDHandleLookup>(); + if (id_handle_lookup == nullptr) { + r_normals.fill_indices(mask.indices(), fallback); + return; + } + + group_indices_by_same_value( + mask.indices(), + surface_hooks, + [&](SurfaceHook base_hook, IndexMask indices_on_same_surface) { + if (base_hook.type() != BKE::SurfaceHookType::MeshObject) { + r_normals.fill_indices(indices_on_same_surface, fallback); + return; + } + + Object *object = id_handle_lookup->lookup(base_hook.object_handle()); + if (object == nullptr) { + r_normals.fill_indices(indices_on_same_surface, fallback); + return; + } + + Mesh *mesh = (Mesh *)object->data; + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(mesh); + int triangle_amount = BKE_mesh_runtime_looptri_len(mesh); + + for (uint i : indices_on_same_surface) { + SurfaceHook hook = surface_hooks[i]; + + if (hook.triangle_index() >= triangle_amount) { + r_normals[i] = fallback; + continue; + } + + const MLoopTri &triangle = triangles[hook.triangle_index()]; + float3 v1 = short_normal_to_float3(mesh->mvert[mesh->mloop[triangle.tri[0]].v].no); + float3 v2 = short_normal_to_float3(mesh->mvert[mesh->mloop[triangle.tri[1]].v].no); + float3 v3 = short_normal_to_float3(mesh->mvert[mesh->mloop[triangle.tri[2]].v].no); + + float3 position; + interp_v3_v3v3v3(position, v1, v2, v3, hook.bary_coords()); + float4x4 local_to_world = object->obmat; + position = local_to_world.transform_direction(position); + + r_normals[i] = position; + } + }, + SurfaceHook::on_same_surface); +} + +MF_GetWeightOnSurface::MF_GetWeightOnSurface() +{ + MFSignatureBuilder signature = this->get_builder("Get Weight on Surface"); + signature.use_global_context<IDHandleLookup>(); + signature.single_input<SurfaceHook>("Surface Hook"); + signature.single_input<std::string>("Group Name"); + signature.single_output<float>("Weight"); +} + +void MF_GetWeightOnSurface::call(IndexMask mask, MFParams params, MFContext context) const +{ + VirtualListRef<SurfaceHook> surface_hooks = params.readonly_single_input<SurfaceHook>( + 0, "Surface Hook"); + VirtualListRef<std::string> group_names = params.readonly_single_input<std::string>( + 1, "Group Name"); + MutableArrayRef<float> r_weights = params.uninitialized_single_output<float>(2, "Weight"); + + float fallback = 0.0f; + + auto *id_handle_lookup = context.try_find_global<IDHandleLookup>(); + if (id_handle_lookup == nullptr) { + r_weights.fill_indices(mask.indices(), fallback); + return; + } + + group_indices_by_same_value( + mask.indices(), + surface_hooks, + [&](SurfaceHook base_hook, IndexMask indices_on_same_surface) { + if (base_hook.type() != BKE::SurfaceHookType::MeshObject) { + r_weights.fill_indices(indices_on_same_surface, fallback); + return; + } + + Object *object = id_handle_lookup->lookup(base_hook.object_handle()); + if (object == nullptr) { + r_weights.fill_indices(indices_on_same_surface, fallback); + return; + } + + Mesh *mesh = (Mesh *)object->data; + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(mesh); + int triangle_amount = BKE_mesh_runtime_looptri_len(mesh); + + group_indices_by_same_value( + indices_on_same_surface, + group_names, + [&](const std::string &group, IndexMask indices_with_same_group) { + MDeformVert *vertex_weights = mesh->dvert; + int group_index = BKE_object_defgroup_name_index(object, group.c_str()); + if (group_index == -1 || vertex_weights == nullptr) { + r_weights.fill_indices(indices_on_same_surface, fallback); + return; + } + for (uint i : indices_with_same_group) { + SurfaceHook hook = surface_hooks[i]; + + if (hook.triangle_index() >= triangle_amount) { + r_weights[i] = fallback; + continue; + } + + const MLoopTri &triangle = triangles[hook.triangle_index()]; + uint v1 = mesh->mloop[triangle.tri[0]].v; + uint v2 = mesh->mloop[triangle.tri[1]].v; + uint v3 = mesh->mloop[triangle.tri[2]].v; + + float3 corner_weights{BKE_defvert_find_weight(vertex_weights + v1, group_index), + BKE_defvert_find_weight(vertex_weights + v2, group_index), + BKE_defvert_find_weight(vertex_weights + v3, group_index)}; + + float weight = float3::dot(hook.bary_coords(), corner_weights); + r_weights[i] = weight; + } + }); + }, + SurfaceHook::on_same_surface); +} + +MF_GetImageColorOnSurface::MF_GetImageColorOnSurface() +{ + MFSignatureBuilder signature = this->get_builder("Get Image Color on Surface"); + signature.use_global_context<IDHandleLookup>(); + signature.single_input<SurfaceHook>("Surface Hook"); + signature.single_input<ImageIDHandle>("Image"); + signature.single_output<rgba_f>("Color"); +} + +static void get_colors_on_surface(IndexMask indices, + VirtualListRef<SurfaceHook> surface_hooks, + MutableArrayRef<rgba_f> r_colors, + rgba_f fallback, + const IDHandleLookup &id_handle_lookup, + const ImBuf &ibuf) +{ + group_indices_by_same_value( + indices, + surface_hooks, + [&](SurfaceHook base_hook, IndexMask indices_on_same_surface) { + if (base_hook.type() != BKE::SurfaceHookType::MeshObject) { + r_colors.fill_indices(indices_on_same_surface, fallback); + return; + } + + Object *object = id_handle_lookup.lookup(base_hook.object_handle()); + if (object == nullptr) { + r_colors.fill_indices(indices_on_same_surface, fallback); + return; + } + + Mesh *mesh = (Mesh *)object->data; + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(mesh); + int triangle_amount = BKE_mesh_runtime_looptri_len(mesh); + + int uv_layer_index = 0; + ArrayRef<MLoopUV> uv_layer = BLI::ref_c_array( + (MLoopUV *)CustomData_get_layer_n(&mesh->ldata, CD_MLOOPUV, uv_layer_index), + mesh->totloop); + + ArrayRef<rgba_b> pixel_buffer = BLI::ref_c_array((rgba_b *)ibuf.rect, ibuf.x * ibuf.y); + + for (uint i : indices_on_same_surface) { + SurfaceHook hook = surface_hooks[i]; + if (hook.triangle_index() >= triangle_amount) { + r_colors[i] = fallback; + continue; + } + + const MLoopTri &triangle = triangles[hook.triangle_index()]; + + float2 uv1 = uv_layer[triangle.tri[0]].uv; + float2 uv2 = uv_layer[triangle.tri[1]].uv; + float2 uv3 = uv_layer[triangle.tri[2]].uv; + + float2 uv; + interp_v2_v2v2v2(uv, uv1, uv2, uv3, hook.bary_coords()); + + uv = uv.clamped_01(); + uint x = uv.x * (ibuf.x - 1); + uint y = uv.y * (ibuf.y - 1); + rgba_b color = pixel_buffer[y * ibuf.x + x]; + r_colors[i] = color; + } + }, + SurfaceHook::on_same_surface); +} + +void MF_GetImageColorOnSurface::call(IndexMask mask, MFParams params, MFContext context) const +{ + if (mask.size() == 0) { + return; + } + + VirtualListRef<SurfaceHook> surface_hooks = params.readonly_single_input<SurfaceHook>( + 0, "Surface Hook"); + VirtualListRef<ImageIDHandle> image_handles = params.readonly_single_input<ImageIDHandle>( + 1, "Image"); + MutableArrayRef<rgba_f> r_colors = params.uninitialized_single_output<rgba_f>(2, "Color"); + + rgba_f fallback = {0.0f, 0.0f, 0.0f, 1.0f}; + + auto *id_handle_lookup = context.try_find_global<IDHandleLookup>(); + if (id_handle_lookup == nullptr) { + r_colors.fill_indices(mask.indices(), fallback); + return; + } + + group_indices_by_same_value<ImageIDHandle>( + mask.indices(), + image_handles, + [&](ImageIDHandle image_handle, IndexMask indices_with_image) { + Image *image = id_handle_lookup->lookup(image_handle); + if (image == nullptr) { + r_colors.fill_indices(indices_with_image, fallback); + return; + } + + ImageUser image_user = {0}; + image_user.ok = true; + ImBuf *ibuf = BKE_image_acquire_ibuf(image, &image_user, NULL); + + get_colors_on_surface( + indices_with_image, surface_hooks, r_colors, fallback, *id_handle_lookup, *ibuf); + + BKE_image_release_ibuf(image, ibuf, NULL); + }); +} + +MF_SampleObjectSurface::MF_SampleObjectSurface(bool use_vertex_weights) + : m_use_vertex_weights(use_vertex_weights) +{ + MFSignatureBuilder signature = this->get_builder("Sample Object Surface"); + signature.use_global_context<IDHandleLookup>(); + signature.single_input<ObjectIDHandle>("Object"); + signature.single_input<int>("Amount"); + signature.single_input<int>("Seed"); + if (use_vertex_weights) { + signature.single_input<std::string>("Vertex Group Name"); + } + signature.vector_output<SurfaceHook>("Surface Hooks"); +} + +static BLI_NOINLINE void compute_triangle_areas(Mesh *mesh, + ArrayRef<MLoopTri> triangles, + MutableArrayRef<float> r_areas) +{ + BLI::assert_same_size(triangles, r_areas); + + for (uint i : triangles.index_range()) { + const MLoopTri &triangle = triangles[i]; + + float3 v1 = mesh->mvert[mesh->mloop[triangle.tri[0]].v].co; + float3 v2 = mesh->mvert[mesh->mloop[triangle.tri[1]].v].co; + float3 v3 = mesh->mvert[mesh->mloop[triangle.tri[2]].v].co; + + float area = area_tri_v3(v1, v2, v3); + r_areas[i] = area; + } +} + +static float3 random_uniform_bary_coords(RNG *rng) +{ + float rand1 = BLI_rng_get_float(rng); + float rand2 = BLI_rng_get_float(rng); + + if (rand1 + rand2 > 1.0f) { + rand1 = 1.0f - rand1; + rand2 = 1.0f - rand2; + } + + return float3(rand1, rand2, 1.0f - rand1 - rand2); +} + +static BLI_NOINLINE void compute_random_uniform_bary_coords( + RNG *rng, MutableArrayRef<float3> r_sampled_bary_coords) +{ + for (float3 &bary_coords : r_sampled_bary_coords) { + bary_coords = random_uniform_bary_coords(rng); + } +} + +static BLI_NOINLINE bool get_vertex_weights(Object *object, + StringRefNull group_name, + MutableArrayRef<float> r_vertex_weights) +{ + Mesh *mesh = (Mesh *)object->data; + BLI_assert(r_vertex_weights.size() == mesh->totvert); + + MDeformVert *vertices = mesh->dvert; + int group_index = BKE_object_defgroup_name_index(object, group_name.data()); + if (group_index == -1 || vertices == nullptr) { + return false; + } + + for (uint i : r_vertex_weights.index_range()) { + r_vertex_weights[i] = BKE_defvert_find_weight(vertices + i, group_index); + } + return true; +} + +static BLI_NOINLINE void vertex_weights_to_triangle_weights( + Mesh *mesh, + ArrayRef<MLoopTri> triangles, + ArrayRef<float> vertex_weights, + MutableArrayRef<float> r_triangle_weights) +{ + BLI::assert_same_size(r_triangle_weights, triangles); + BLI_assert(mesh->totvert == vertex_weights.size()); + + for (uint triangle_index : triangles.index_range()) { + const MLoopTri &looptri = triangles[triangle_index]; + float triangle_weight = 0.0f; + for (uint i = 0; i < 3; i++) { + uint vertex_index = mesh->mloop[looptri.tri[i]].v; + float weight = vertex_weights[vertex_index]; + triangle_weight += weight; + } + + r_triangle_weights[triangle_index] = triangle_weight / 3.0f; + } +} + +void MF_SampleObjectSurface::call(IndexMask mask, MFParams params, MFContext context) const +{ + uint param_index = 0; + VirtualListRef<ObjectIDHandle> object_handles = params.readonly_single_input<ObjectIDHandle>( + param_index++, "Object"); + VirtualListRef<int> amounts = params.readonly_single_input<int>(param_index++, "Amount"); + VirtualListRef<int> seeds = params.readonly_single_input<int>(param_index++, "Seed"); + VirtualListRef<std::string> vertex_group_names; + if (m_use_vertex_weights) { + vertex_group_names = params.readonly_single_input<std::string>(param_index++, + "Vertex Group Name"); + } + GenericVectorArray::MutableTypedRef<SurfaceHook> r_hooks_per_index = + params.vector_output<SurfaceHook>(param_index++, "Surface Hooks"); + + const IDHandleLookup *id_handle_lookup = context.try_find_global<IDHandleLookup>(); + if (id_handle_lookup == nullptr) { + return; + } + + RNG *rng = BLI_rng_new(0); + + for (uint i : mask.indices()) { + uint amount = (uint)std::max<int>(amounts[i], 0); + if (amount == 0) { + continue; + } + + ObjectIDHandle object_handle = object_handles[i]; + Object *object = id_handle_lookup->lookup(object_handle); + if (object == nullptr && object->type != OB_MESH) { + continue; + } + + Mesh *mesh = (Mesh *)object->data; + const MLoopTri *triangles_buffer = BKE_mesh_runtime_looptri_ensure(mesh); + ArrayRef<MLoopTri> triangles(triangles_buffer, BKE_mesh_runtime_looptri_len(mesh)); + if (triangles.size() == 0) { + continue; + } + + Array<float> triangle_weights(triangles.size()); + compute_triangle_areas(mesh, triangles, triangle_weights); + + if (m_use_vertex_weights) { + Array<float> vertex_weights(mesh->totvert); + if (get_vertex_weights(object, vertex_group_names[i], vertex_weights)) { + Array<float> vertex_weights_for_triangles(triangles.size()); + vertex_weights_to_triangle_weights( + mesh, triangles, vertex_weights, vertex_weights_for_triangles); + + for (uint i : triangle_weights.index_range()) { + triangle_weights[i] *= vertex_weights_for_triangles[i]; + } + } + } + + Array<float> cumulative_weights(triangle_weights.size() + 1); + float total_weight = compute_cumulative_distribution(triangle_weights, cumulative_weights); + if (total_weight <= 0.0f) { + continue; + } + + BLI_rng_srandom(rng, seeds[i] + amount * 1000); + Array<uint> triangle_indices(amount); + sample_cumulative_distribution(rng, cumulative_weights, triangle_indices); + + Array<float3> bary_coords(amount); + compute_random_uniform_bary_coords(rng, bary_coords); + + MutableArrayRef<SurfaceHook> r_hooks = r_hooks_per_index.allocate_and_default_construct( + i, amount); + for (uint i : IndexRange(amount)) { + r_hooks[i] = SurfaceHook(object_handle, triangle_indices[i], bary_coords[i]); + } + } + + BLI_rng_free(rng); +} + +} // namespace FN |