Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/texture_field.cc
- This file was added.
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |||||
| #include "BLI_math_vec_types.hh" | |||||
| #include "DNA_mesh_types.h" | |||||
| #include "DNA_meshdata_types.h" | |||||
| #include "BKE_attribute.h" | |||||
| #include "BKE_mesh.h" | |||||
| #include "BKE_mesh_runtime.h" | |||||
| #include "BKE_texture_field.hh" | |||||
| #include "NOD_texture_evaluate.hh" | |||||
| #include "attribute_access_intern.hh" | |||||
| using blender::ColorGeometry4b; | |||||
| using blender::ColorGeometry4f; | |||||
| using blender::float2; | |||||
| using blender::float3; | |||||
| using blender::float4; | |||||
| using blender::nodes::GeometrySurfaceTextureBatch; | |||||
| using blender::nodes::TextureBatch; | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Geometry Component Implementation | |||||
| * \{ */ | |||||
| namespace blender::bke { | |||||
| TextureFieldContext::TextureFieldContext() | |||||
| { | |||||
| } | |||||
| TextureFieldContext::TextureFieldContext(const GeometrySurfaceTextureBatch &batch) : batch_(&batch) | |||||
| { | |||||
| mesh_component_ = std::make_unique<MeshComponent>(); | |||||
| mesh_component_->replace(const_cast<Mesh *>(batch.mesh), GeometryOwnershipType::ReadOnly); | |||||
| } | |||||
| TextureFieldContext::TextureFieldContext(const TextureBatch &batch) : batch_(&batch) | |||||
| { | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Attribute Interpolation | |||||
| * \{ */ | |||||
| /* TODO: dedup with data transfer? | |||||
| * TODO: datatype conversions are inconsistent with geometry nodes. */ | |||||
| template<typename T, typename OutT = T> inline OutT read_attribute(const T &value) | |||||
| { | |||||
| return value; | |||||
| } | |||||
| inline float4 read_attribute(const ColorGeometry4f &value) | |||||
| { | |||||
| return float4(value); | |||||
| } | |||||
| inline float4 read_attribute(const ColorGeometry4b &value) | |||||
| { | |||||
| return read_attribute(value.decode()); | |||||
| } | |||||
| inline float2 read_attribute(const MLoopUV &value) | |||||
| { | |||||
| return float2(value.uv[0], value.uv[1]); | |||||
| } | |||||
| template<typename OutT, typename T = OutT> inline OutT write_attribute(const T &value) | |||||
| { | |||||
| return value; | |||||
| } | |||||
| template<> inline ColorGeometry4f write_attribute(const float4 &value) | |||||
| { | |||||
| return ColorGeometry4f(value); | |||||
| } | |||||
| template<> inline float3 write_attribute(const float2 &value) | |||||
| { | |||||
| return float3(value[0], value[1], 0.0f); | |||||
| } | |||||
| template<typename T, typename OutT> class GVArray_ForMeshSurface : public GVArrayImpl { | |||||
| protected: | |||||
| const GeometrySurfaceTextureBatch::Sample *samples_; | |||||
| const MLoop *mloop_; | |||||
| const MLoopTri *mlooptri_; | |||||
| const T *data_; | |||||
| public: | |||||
| GVArray_ForMeshSurface(const GeometrySurfaceTextureBatch &batch, const void *data) | |||||
| : GVArrayImpl(CPPType::get<OutT>(), batch.size), | |||||
| samples_(batch.samples.data()), | |||||
| mloop_(batch.mesh->mloop), | |||||
| mlooptri_(BKE_mesh_runtime_looptri_ensure(batch.mesh)), | |||||
| data_(static_cast<const T *>(data)) | |||||
| { | |||||
| } | |||||
| }; | |||||
| template<typename T, typename OutT> | |||||
| class GVArray_ForMeshSurfaceVertexAttribute : public GVArray_ForMeshSurface<T, OutT> { | |||||
| public: | |||||
| GVArray_ForMeshSurfaceVertexAttribute(const GeometrySurfaceTextureBatch &batch, const void *data) | |||||
| : GVArray_ForMeshSurface<T, OutT>(batch, data) | |||||
| { | |||||
| } | |||||
| protected: | |||||
| /* TODO: optimize for batches? */ | |||||
| void get(const int64_t index, void *r_value) const override | |||||
| { | |||||
| get_to_uninitialized(index, r_value); | |||||
| } | |||||
| void get_to_uninitialized(const int64_t index, void *r_value) const override | |||||
| { | |||||
| const GeometrySurfaceTextureBatch::Sample sample = this->samples_[index]; | |||||
| const MLoopTri &tri = this->mlooptri_[sample.triangle]; | |||||
| const float u = sample.u; | |||||
| const float v = sample.v; | |||||
| const int v0 = this->mloop_[tri.tri[0]].v; | |||||
| const int v1 = this->mloop_[tri.tri[1]].v; | |||||
| const int v2 = this->mloop_[tri.tri[2]].v; | |||||
| *(OutT *)r_value = write_attribute<OutT>(read_attribute(this->data_[v0]) * u + | |||||
| read_attribute(this->data_[v1]) * v + | |||||
| read_attribute(this->data_[v2]) * (1.0f - u - v)); | |||||
| } | |||||
| }; | |||||
| template<typename T, typename OutT> | |||||
| class GVArray_ForMeshSurfaceFaceAttribute : public GVArray_ForMeshSurface<T, OutT> { | |||||
| public: | |||||
| GVArray_ForMeshSurfaceFaceAttribute(const GeometrySurfaceTextureBatch &batch, const void *data) | |||||
| : GVArray_ForMeshSurface<T, OutT>(batch, data) | |||||
| { | |||||
| } | |||||
| protected: | |||||
| void get(const int64_t index, void *r_value) const override | |||||
| { | |||||
| get_to_uninitialized(index, r_value); | |||||
| } | |||||
| void get_to_uninitialized(const int64_t index, void *r_value) const override | |||||
| { | |||||
| const GeometrySurfaceTextureBatch::Sample sample = this->samples_[index]; | |||||
| const MLoopTri &tri = this->mlooptri_[sample.triangle]; | |||||
| *(OutT *)r_value = write_attribute<OutT>(read_attribute(this->data_[tri.poly])); | |||||
| } | |||||
| }; | |||||
| template<typename T, typename OutT> | |||||
| class GVArray_ForMeshSurfaceCornerAttribute : public GVArray_ForMeshSurface<T, OutT> { | |||||
| public: | |||||
| GVArray_ForMeshSurfaceCornerAttribute(const GeometrySurfaceTextureBatch &batch, const void *data) | |||||
| : GVArray_ForMeshSurface<T, OutT>(batch, data) | |||||
| { | |||||
| } | |||||
| protected: | |||||
| void get(const int64_t index, void *r_value) const override | |||||
| { | |||||
| get_to_uninitialized(index, r_value); | |||||
| } | |||||
| void get_to_uninitialized(const int64_t index, void *r_value) const override | |||||
| { | |||||
| const GeometrySurfaceTextureBatch::Sample sample = this->samples_[index]; | |||||
| const MLoopTri &tri = this->mlooptri_[sample.triangle]; | |||||
| const float u = sample.u; | |||||
| const float v = sample.v; | |||||
| const int v0 = tri.tri[0]; | |||||
| const int v1 = tri.tri[1]; | |||||
| const int v2 = tri.tri[2]; | |||||
| *(OutT *)r_value = write_attribute<OutT>(read_attribute(this->data_[v0]) * u + | |||||
| read_attribute(this->data_[v1]) * v + | |||||
| read_attribute(this->data_[v2]) * (1.0f - u - v)); | |||||
| } | |||||
| }; | |||||
| /* TODO: loop normals support. */ | |||||
| class GVArray_ForMeshSurfaceNormal : public GVArray_ForMeshSurface<float3, float3> { | |||||
| protected: | |||||
| const MPoly *mpoly_; | |||||
| const float3 *poly_normals_; | |||||
| public: | |||||
| GVArray_ForMeshSurfaceNormal(const GeometrySurfaceTextureBatch &batch) | |||||
| : GVArray_ForMeshSurface<float3, float3>( | |||||
| batch, (const float3 *)(BKE_mesh_vertex_normals_ensure(batch.mesh))), | |||||
| mpoly_(batch.mesh->mpoly), | |||||
| poly_normals_((const float3 *)(BKE_mesh_poly_normals_ensure(batch.mesh))) | |||||
| { | |||||
| } | |||||
| protected: | |||||
| void get(const int64_t index, void *r_value) const override | |||||
| { | |||||
| get_to_uninitialized(index, r_value); | |||||
| } | |||||
| void get_to_uninitialized(const int64_t index, void *r_value) const override | |||||
| { | |||||
| const GeometrySurfaceTextureBatch::Sample sample = this->samples_[index]; | |||||
| const MLoopTri &tri = this->mlooptri_[sample.triangle]; | |||||
| if (this->mpoly_[tri.poly].flag & ME_SMOOTH) { | |||||
| const float u = sample.u; | |||||
| const float v = sample.v; | |||||
| const int v0 = this->mloop_[tri.tri[0]].v; | |||||
| const int v1 = this->mloop_[tri.tri[1]].v; | |||||
| const int v2 = this->mloop_[tri.tri[2]].v; | |||||
| *(float3 *)r_value = math::normalize(read_attribute(this->data_[v0]) * u + | |||||
| read_attribute(this->data_[v1]) * v + | |||||
| read_attribute(this->data_[v2]) * (1.0f - u - v)); | |||||
| } | |||||
| else { | |||||
| *(float3 *)r_value = this->poly_normals_[tri.poly]; | |||||
| } | |||||
| } | |||||
| }; | |||||
| class GVArray_ForMeshSurfaceFaceNormal : public GVArray_ForMeshSurface<float3, float3> { | |||||
| public: | |||||
| GVArray_ForMeshSurfaceFaceNormal(const GeometrySurfaceTextureBatch &batch) | |||||
| : GVArray_ForMeshSurface<float3, float3>( | |||||
| batch, (const float3 *)(BKE_mesh_poly_normals_ensure(batch.mesh))) | |||||
| { | |||||
| } | |||||
| protected: | |||||
| void get(const int64_t index, void *r_value) const override | |||||
| { | |||||
| get_to_uninitialized(index, r_value); | |||||
| } | |||||
| void get_to_uninitialized(const int64_t index, void *r_value) const override | |||||
| { | |||||
| const GeometrySurfaceTextureBatch::Sample sample = this->samples_[index]; | |||||
| const MLoopTri &tri = this->mlooptri_[sample.triangle]; | |||||
| *(float3 *)r_value = read_attribute(this->data_[tri.poly]); | |||||
| } | |||||
| }; | |||||
| class GVArray_ForMeshSurfaceParametric : public GVArrayImpl { | |||||
| protected: | |||||
| const GeometrySurfaceTextureBatch::Sample *samples_; | |||||
| public: | |||||
| GVArray_ForMeshSurfaceParametric(const GeometrySurfaceTextureBatch &batch) | |||||
| : GVArrayImpl(CPPType::get<float3>(), batch.size), samples_(batch.samples.data()) | |||||
| { | |||||
| } | |||||
| protected: | |||||
| void get(const int64_t index, void *r_value) const override | |||||
| { | |||||
| get_to_uninitialized(index, r_value); | |||||
| } | |||||
| void get_to_uninitialized(const int64_t index, void *r_value) const override | |||||
| { | |||||
| const GeometrySurfaceTextureBatch::Sample sample = this->samples_[index]; | |||||
| *(float3 *)r_value = float3(sample.u, sample.v, 0.0f); | |||||
| } | |||||
| }; | |||||
| template<template<typename T, typename OutT> class GVArrayImplT> | |||||
| static GVArray surface_attribute_varray(const GeometrySurfaceTextureBatch &batch, | |||||
| const CustomDataLayer *layer) | |||||
| { | |||||
| const void *data = layer->data; | |||||
| switch (layer->type) { | |||||
| case CD_PROP_FLOAT: | |||||
| return GVArray::For<GVArrayImplT<float, float>>(batch, data); | |||||
| case CD_PROP_INT32: | |||||
| return GVArray::For<GVArrayImplT<int, float>>(batch, data); | |||||
| case CD_PROP_FLOAT3: | |||||
| return GVArray::For<GVArrayImplT<float3, float3>>(batch, data); | |||||
| case CD_PROP_COLOR: | |||||
| return GVArray::For<GVArrayImplT<ColorGeometry4f, ColorGeometry4f>>(batch, data); | |||||
| case CD_PROP_BYTE_COLOR: | |||||
| return GVArray::For<GVArrayImplT<ColorGeometry4b, ColorGeometry4f>>(batch, data); | |||||
| case CD_MLOOPUV: | |||||
| return GVArray::For<GVArrayImplT<MLoopUV, float3>>(batch, data); | |||||
| case CD_PROP_BOOL: | |||||
| return GVArray::For<GVArrayImplT<bool, float>>(batch, data); | |||||
| case CD_PROP_FLOAT2: | |||||
| return GVArray::For<GVArrayImplT<float2, float3>>(batch, data); | |||||
| case CD_PROP_INT8: | |||||
| return GVArray::For<GVArrayImplT<int8_t, float>>(batch, data); | |||||
| default: | |||||
| return GVArray(); | |||||
| } | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Attribute Providers | |||||
| * \{ */ | |||||
| class TextureBuiltinAttributeProvider : public BuiltinAttributeProvider { | |||||
| public: | |||||
| TextureBuiltinAttributeProvider(StringRefNull name, eCustomDataType type) | |||||
| : BuiltinAttributeProvider( | |||||
| name, ATTR_DOMAIN_TEXTURE, type, NonCreatable, Readonly, NonDeletable) | |||||
| { | |||||
| } | |||||
| GAttributeWriter try_get_for_write(void *UNUSED(owner)) const final | |||||
| { | |||||
| BLI_assert_unreachable(); | |||||
| return {}; | |||||
| } | |||||
| bool try_delete(void *UNUSED(owner)) const final | |||||
| { | |||||
| return false; | |||||
| } | |||||
| bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final | |||||
| { | |||||
| return false; | |||||
| } | |||||
| bool exists(const void *owner) const final | |||||
| { | |||||
| return true; | |||||
| } | |||||
| }; | |||||
| class TexturePositionAttributeProvider final : public TextureBuiltinAttributeProvider { | |||||
| public: | |||||
| TexturePositionAttributeProvider() : TextureBuiltinAttributeProvider("position", CD_PROP_FLOAT3) | |||||
| { | |||||
| } | |||||
| GVArray try_get_for_read(const void *owner) const final | |||||
| { | |||||
| const TextureFieldContext &texture_context = *static_cast<const TextureFieldContext *>(owner); | |||||
| return GVArray::ForSpan(texture_context.batch()->positions.as_span()); | |||||
| } | |||||
| }; | |||||
| class TextureNormalAttributeProvider final : public TextureBuiltinAttributeProvider { | |||||
| public: | |||||
| TextureNormalAttributeProvider() : TextureBuiltinAttributeProvider("normal", CD_PROP_FLOAT3) | |||||
| { | |||||
| } | |||||
| GVArray try_get_for_read(const void *owner) const final | |||||
| { | |||||
| const TextureFieldContext &texture_context = *static_cast<const TextureFieldContext *>(owner); | |||||
| const GeometrySurfaceTextureBatch &batch = static_cast<const GeometrySurfaceTextureBatch &>( | |||||
| *(texture_context.batch())); | |||||
| return GVArray::For<GVArray_ForMeshSurfaceNormal>(batch); | |||||
| } | |||||
| }; | |||||
| /* TODO: deduplicate parts with CustomDataAttributeProvider. */ | |||||
| class TextureCustomDataAttributeProvider final : public DynamicAttributesProvider { | |||||
| private: | |||||
| static constexpr uint64_t supported_types_mask = CD_MASK_PROP_ALL | CD_MASK_MLOOPUV; | |||||
| public: | |||||
| TextureCustomDataAttributeProvider() | |||||
| { | |||||
| } | |||||
| GAttributeReader try_get_for_read(const void *owner, | |||||
| const AttributeIDRef &attribute_id) const final | |||||
| { | |||||
| if (!attribute_id.is_named()) { | |||||
| return {}; | |||||
| } | |||||
| const TextureFieldContext &texture_context = *static_cast<const TextureFieldContext *>(owner); | |||||
| const GeometrySurfaceTextureBatch &batch = static_cast<const GeometrySurfaceTextureBatch &>( | |||||
| *(texture_context.batch())); | |||||
| /* TODO: move many of these to MeshComponent | |||||
| ReadAttributeLookup lookup = | |||||
| texture_context.surface_component()->attribute_try_get_for_read(attribute_id); | |||||
| */ | |||||
| /* TODO: probably these should be builtin providers, but benefit of the additional boilerplate | |||||
| * code is unclear. */ | |||||
| if (attribute_id.name() == "normal") { | |||||
| return {GVArray::For<GVArray_ForMeshSurfaceNormal>(batch), ATTR_DOMAIN_TEXTURE}; | |||||
| } | |||||
| else if (attribute_id.name() == "rest_position") { | |||||
| /* TODO: support in MeshComponent. */ | |||||
| float3 *orco = (float3 *)CustomData_get_layer(&batch.mesh->vdata, CD_ORCO); | |||||
| if (orco) { | |||||
| return {GVArray::For<GVArray_ForMeshSurfaceVertexAttribute<float3, float3>>(batch, orco), | |||||
| ATTR_DOMAIN_TEXTURE}; | |||||
| } | |||||
| else { | |||||
| return {GVArray::ForSpan(batch.positions.as_span()), ATTR_DOMAIN_TEXTURE}; | |||||
| } | |||||
| } | |||||
| else if (attribute_id.name() == ".face_normal") { | |||||
| /* TODO: support in MeshComponent. */ | |||||
| return {GVArray::For<GVArray_ForMeshSurfaceFaceNormal>(batch), ATTR_DOMAIN_TEXTURE}; | |||||
| } | |||||
| else if (attribute_id.name() == ".parametric") { | |||||
| return {GVArray::For<GVArray_ForMeshSurfaceParametric>(batch), ATTR_DOMAIN_TEXTURE}; | |||||
| } | |||||
| else if (attribute_id.name() == ".uv_active") { | |||||
| /* TODO: support in MeshComponent. */ | |||||
| MLoopUV *uv = batch.mesh->mloopuv; | |||||
| if (uv) { | |||||
| return {GVArray::For<GVArray_ForMeshSurfaceCornerAttribute<MLoopUV, float3>>(batch, uv), | |||||
| ATTR_DOMAIN_TEXTURE}; | |||||
| } | |||||
| } | |||||
| else if (attribute_id.name() == ".tangent") { | |||||
| /* TODO: support here and in MeshComponent. */ | |||||
| } | |||||
| else if (attribute_id.name() == ".pointiness") { | |||||
| /* TODO: support here and in MeshComponent. */ | |||||
| } | |||||
| else if (attribute_id.name() == ".random_per_island") { | |||||
| /* TODO: support here and in MeshComponent. */ | |||||
| } | |||||
| else { | |||||
| std::string lookup_name(attribute_id.name()); | |||||
| CustomDataLayer *layer = (lookup_name == ".color_active") ? | |||||
| BKE_id_attributes_render_color_get(&batch.mesh->id) : | |||||
| BKE_id_attribute_search(const_cast<ID *>(&batch.mesh->id), | |||||
| lookup_name.c_str(), | |||||
| supported_types_mask, | |||||
| ATTR_DOMAIN_MASK_ALL); | |||||
| if (layer == nullptr) { | |||||
| return {}; | |||||
| } | |||||
| switch (BKE_id_attribute_domain(&batch.mesh->id, layer)) { | |||||
| case ATTR_DOMAIN_POINT: | |||||
| return {surface_attribute_varray<GVArray_ForMeshSurfaceVertexAttribute>(batch, layer), | |||||
| ATTR_DOMAIN_TEXTURE}; | |||||
| case ATTR_DOMAIN_EDGE: | |||||
| /* TODO: support edge domain. */ | |||||
| BLI_assert_unreachable(); | |||||
| break; | |||||
| case ATTR_DOMAIN_FACE: | |||||
| return {surface_attribute_varray<GVArray_ForMeshSurfaceFaceAttribute>(batch, layer), | |||||
| ATTR_DOMAIN_TEXTURE}; | |||||
| case ATTR_DOMAIN_CORNER: | |||||
| return {surface_attribute_varray<GVArray_ForMeshSurfaceCornerAttribute>(batch, layer), | |||||
| ATTR_DOMAIN_TEXTURE}; | |||||
| case ATTR_DOMAIN_CURVE: | |||||
| case ATTR_DOMAIN_INSTANCE: | |||||
| case ATTR_DOMAIN_AUTO: | |||||
| case ATTR_DOMAIN_TEXTURE: | |||||
| BLI_assert_unreachable(); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return {}; | |||||
| } | |||||
| GAttributeWriter try_get_for_write(void *UNUSED(owner), const AttributeIDRef &) const final | |||||
| { | |||||
| BLI_assert_unreachable(); | |||||
| return {}; | |||||
| } | |||||
| bool try_delete(void *, const AttributeIDRef &) const final | |||||
| { | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| bool try_create(void *, | |||||
| const AttributeIDRef &, | |||||
| eAttrDomain, | |||||
| const eCustomDataType, | |||||
| const AttributeInit &) const final | |||||
| { | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| bool foreach_attribute(const void *, const AttributeForeachCallback) const final | |||||
| { | |||||
| BLI_assert_unreachable(); | |||||
| return true; | |||||
| } | |||||
| void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final | |||||
| { | |||||
| callback(ATTR_DOMAIN_TEXTURE); | |||||
| } | |||||
| private: | |||||
| bool type_is_supported(eCustomDataType data_type) const | |||||
| { | |||||
| return ((1ULL << data_type) & supported_types_mask) != 0; | |||||
| } | |||||
| }; | |||||
| std::optional<blender::bke::AttributeAccessor> TextureFieldContext::attributes() const | |||||
| { | |||||
| static blender::bke::TexturePositionAttributeProvider position; | |||||
| static blender::bke::TextureNormalAttributeProvider normal; | |||||
| static blender::bke::TextureCustomDataAttributeProvider custom_data; | |||||
| if (mesh_component_) { | |||||
| /* The attributes supported here must be a subset of MeshComponent, and include all the | |||||
| * attributes used by texture nodes. */ | |||||
| static blender::bke::ComponentAttributeProviders providers = | |||||
| blender::bke::ComponentAttributeProviders({&position, &normal}, {&custom_data}); | |||||
| static AttributeAccessorFunctions fn = | |||||
| attribute_accessor_functions::accessor_functions_for_providers<providers>(); | |||||
| return blender::bke::AttributeAccessor(this, fn); | |||||
| } | |||||
| else { | |||||
| static blender::bke::ComponentAttributeProviders providers = | |||||
| blender::bke::ComponentAttributeProviders({&position}, {}); | |||||
| static AttributeAccessorFunctions fn = | |||||
| attribute_accessor_functions::accessor_functions_for_providers<providers>(); | |||||
| return blender::bke::AttributeAccessor(this, fn); | |||||
| } | |||||
| } | |||||
| /** \} */ | |||||
| } // namespace blender::bke | |||||