From 1d8ef0bac8fd511aede00703dad94a6d5db15bc0 Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Thu, 7 Dec 2023 17:16:08 +0100 Subject: [PATCH] Added some basic geometry functionality (only tangent generation for now). --- source/mijin/geo/geometry_traits.hpp | 103 ++++++++++++++++++++++++ source/mijin/geo/glm_traits.hpp | 37 +++++++++ source/mijin/geo/tangent_generation.hpp | 102 +++++++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 source/mijin/geo/geometry_traits.hpp create mode 100644 source/mijin/geo/glm_traits.hpp create mode 100644 source/mijin/geo/tangent_generation.hpp diff --git a/source/mijin/geo/geometry_traits.hpp b/source/mijin/geo/geometry_traits.hpp new file mode 100644 index 0000000..aa597d0 --- /dev/null +++ b/source/mijin/geo/geometry_traits.hpp @@ -0,0 +1,103 @@ + + +#pragma once + +#if !defined(MIJIN_GEO_GEOMETRY_TRAITS_HPP_INCLUDED) +#define MIJIN_GEO_GEOMETRY_TRAITS_HPP_INCLUDED 1 + +#include +#include + +namespace mijin +{ +template +struct Vector2Traits; + +template +concept Vector2 = requires(const std::decay_t constObject, std::decay_t nonConstObject) +{ + { mijin::Vector2Traits>{}.getX(constObject) } -> std::convertible_to; + { mijin::Vector2Traits>{}.getY(constObject) } -> std::convertible_to; + { mijin::Vector2Traits>{}.setX(nonConstObject, 0.f) }; + { mijin::Vector2Traits>{}.setY(nonConstObject, 0.f) }; +}; + +template +struct Vector3Traits; + +template +concept Vector3 = requires(const std::decay_t constObject, std::decay_t nonConstObject) +{ + { mijin::Vector3Traits>{}.getX(constObject) } -> std::convertible_to; + { mijin::Vector3Traits>{}.getY(constObject) } -> std::convertible_to; + { mijin::Vector3Traits>{}.getZ(constObject) } -> std::convertible_to; + { mijin::Vector3Traits>{}.setX(nonConstObject, 0.f) }; + { mijin::Vector3Traits>{}.setY(nonConstObject, 0.f) }; + { mijin::Vector3Traits>{}.setZ(nonConstObject, 0.f) }; +}; + +template +concept Index = std::convertible_to; + +template +concept Vector2Iterator = requires(T object) +{ + { std::iterator_traits::value_type } -> Vector2; +}; + +template +concept Vector3Iterator = requires(T object) +{ + { std::iterator_traits::value_type } -> Vector3; +}; + +template +concept Vector2Range = std::ranges::random_access_range && Vector2>; + +template +concept Vector3Range = std::ranges::random_access_range && Vector3>; + +template +concept IndexRange = std::ranges::random_access_range && Index>; + +template +struct Mesh3DTraits; + +template +concept Mesh3D = requires(const T constObject, T nonConstObject, mijin::Mesh3DTraits::index_t index, mijin::Mesh3DTraits::vector3_t vec3Value) +{ + { typename mijin::Mesh3DTraits::index_t() } -> Index; + { typename mijin::Mesh3DTraits::vector3_t() } -> Vector3; + { mijin::Mesh3DTraits{}.getNumFaces(constObject) } -> std::convertible_to; + { mijin::Mesh3DTraits{}.getNumVertices(constObject) } -> std::convertible_to; + { mijin::Mesh3DTraits{}.getFaceNumVertices(constObject, index) } -> std::convertible_to; + { mijin::Mesh3DTraits{}.getPosition(constObject, index, index) } -> Vector3; + { mijin::Mesh3DTraits{}.setPosition(nonConstObject, index, index, vec3Value) }; +}; + +template +concept Mesh3DWithNormals = Mesh3D && requires(const T constObject, T nonConstObject, mijin::Mesh3DTraits::index_t index, mijin::Mesh3DTraits::vector3_t vec3Value) +{ + { mijin::Mesh3DTraits{}.getNormal(constObject, index, index) } -> Vector3; + { mijin::Mesh3DTraits{}.setNormal(nonConstObject, index, index, vec3Value) }; +}; + +template +concept Mesh3DWithTangents = Mesh3D && requires(const T constObject, T nonConstObject, mijin::Mesh3DTraits::index_t index, mijin::Mesh3DTraits::vector3_t vec3Value) +{ + { mijin::Mesh3DTraits{}.getTangent(constObject, index, index) } -> Vector3; + { mijin::Mesh3DTraits{}.setTangent(nonConstObject, index, index, vec3Value) }; +}; + +template +concept Mesh3DWithTexCoords = Mesh3D && requires(const T constObject, T nonConstObject, mijin::Mesh3DTraits::index_t index, mijin::Mesh3DTraits::vector2_t vec2Value) +{ + { mijin::Mesh3DTraits{}.getTexCoord(constObject, index, index) } -> Vector2; + { mijin::Mesh3DTraits{}.setTexCoord(nonConstObject, index, index, vec2Value) }; +}; + +template +concept FullMesh3D = Mesh3DWithNormals && Mesh3DWithTangents && Mesh3DWithTexCoords; +} + +#endif // MIJIN_GEO_GEOMETRY_TRAITS_HPP_INCLUDED diff --git a/source/mijin/geo/glm_traits.hpp b/source/mijin/geo/glm_traits.hpp new file mode 100644 index 0000000..1bc2c82 --- /dev/null +++ b/source/mijin/geo/glm_traits.hpp @@ -0,0 +1,37 @@ + + +#pragma once + +#if !defined(MIJIN_GEO_GLM_ADAPTER_HPP_INCLUDED) +#define MIJIN_GEO_GLM_ADAPTER_HPP_INCLUDED 1 + +#include +#include +#include "./geometry_traits.hpp" + +namespace mijin +{ +template<> +struct Vector2Traits +{ + constexpr float getX(const glm::vec2& vector) const noexcept { return vector.x; } + constexpr float getY(const glm::vec2& vector) const noexcept { return vector.y; } + constexpr void setX(glm::vec2& vector, float value) const noexcept { vector.x = value; } + constexpr void setY(glm::vec2& vector, float value) const noexcept { vector.y = value; } +}; +static_assert(Vector2, "Vector2Adapter not working."); + +template<> +struct Vector3Traits +{ + constexpr float getX(const glm::vec3& vector) const noexcept { return vector.x; } + constexpr float getY(const glm::vec3& vector) const noexcept { return vector.y; } + constexpr float getZ(const glm::vec3& vector) const noexcept { return vector.z; } + constexpr void setX(glm::vec3& vector, float value) const noexcept { vector.x = value; } + constexpr void setY(glm::vec3& vector, float value) const noexcept { vector.y = value; } + constexpr void setZ(glm::vec3& vector, float value) const noexcept { vector.z = value; } +}; +static_assert(Vector3, "Vector3Adapter not working."); +} + +#endif // MIJIN_GEO_GLM_ADAPTER_HPP_INCLUDED diff --git a/source/mijin/geo/tangent_generation.hpp b/source/mijin/geo/tangent_generation.hpp new file mode 100644 index 0000000..ebb8e2e --- /dev/null +++ b/source/mijin/geo/tangent_generation.hpp @@ -0,0 +1,102 @@ + + +#pragma once + +#if !defined(MIJIN_GEO_TANGENT_GENERATION_HPP_INCLUDED) +#define MIJIN_GEO_TANGENT_GENERATION_HPP_INCLUDED 1 + +#include +#include "./geometry_traits.hpp" + +namespace mijin +{ +namespace impl +{ +template +struct MikktData +{ + Mesh3DTraits traits; + Vector2Traits vec2Traits; + Vector3Traits vec3Traits; + TMesh* mesh; +}; + +template +int mikktGetNumFaces(const SMikkTSpaceContext* context) +{ + const MikktData& data = *static_cast*>(context->m_pUserData); + return static_cast(data.traits.getNumFaces(*data.mesh)); +} + +template +int mikktGetNumVerticesOfFace(const SMikkTSpaceContext* context, const int face) +{ + const MikktData& data = *static_cast*>(context->m_pUserData); + return static_cast(data.traits.getFaceNumVertices(*data.mesh, face)); +} + +template +void mikktGetPosition(const SMikkTSpaceContext* context, float posOut[], const int face, const int vert) // NOLINT +{ + const MikktData& data = *static_cast*>(context->m_pUserData); + const auto position = data.traits.getPosition(*data.mesh, face, vert); + posOut[0] = data.vec3Traits.getX(position); + posOut[1] = data.vec3Traits.getY(position); + posOut[2] = data.vec3Traits.getZ(position); +} + +template +void mikktGetNormal(const SMikkTSpaceContext* context, float normOut[], const int face, const int vert) // NOLINT +{ + const MikktData& data = *static_cast*>(context->m_pUserData); + const auto normal = data.traits.getNormal(*data.mesh, face, vert); + normOut[0] = data.vec3Traits.getX(normal); + normOut[1] = data.vec3Traits.getY(normal); + normOut[2] = data.vec3Traits.getZ(normal); +} + +template +void mikktGetTexCoord(const SMikkTSpaceContext* context, float texcOut[], const int face, const int vert) // NOLINT +{ + const MikktData& data = *static_cast*>(context->m_pUserData); + const auto texCoord = data.traits.getTexCoord(*data.mesh, face, vert); + texcOut[0] = data.vec2Traits.getX(texCoord); + texcOut[1] = data.vec2Traits.getY(texCoord); +} + +template +void mikktSetTSpaceBasic(const SMikkTSpaceContext* context, const float resultTangent[], const float sign, const int face, const int vert) // NOLINT +{ + // TODO: what do we do with sign? (it should be used for the direction of the bitangent) + (void) sign; + const MikktData& data = *static_cast*>(context->m_pUserData); + typename Mesh3DTraits::vector3_t tangent; + data.vec3Traits.setX(tangent, resultTangent[0]); + data.vec3Traits.setY(tangent, resultTangent[1]); + data.vec3Traits.setZ(tangent, resultTangent[2]); + data.traits.setTangent(*data.mesh, face, vert, tangent); +} +} +template +bool generateTangentsMikkt(TMesh& mesh) +{ + SMikkTSpaceInterface mikktInterface = { + .m_getNumFaces = &impl::mikktGetNumFaces, + .m_getNumVerticesOfFace = &impl::mikktGetNumVerticesOfFace, + .m_getPosition = &impl::mikktGetPosition, + .m_getNormal = &impl::mikktGetNormal, + .m_getTexCoord = &impl::mikktGetTexCoord, + .m_setTSpaceBasic = &impl::mikktSetTSpaceBasic + }; + impl::MikktData data = { + .mesh = &mesh + }; + const SMikkTSpaceContext context = { + .m_pInterface = &mikktInterface, + .m_pUserData = &data + }; + return genTangSpaceDefault(&context); +} +} + +#endif // MIJIN_GEO_TANGENT_GENERATION_HPP_INCLUDED