From 2a88a885c15b1f6302fb9ccdc09e1e1789b784f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 21 Mar 2020 17:19:08 +0100 Subject: [PATCH] Primitives: tangents in the plane primitive. This turned the primitive from being fully defined at compile time to being mostly dynamically allocated. Keeping just the positions+normals case defined at compile time, and splitting the function into two overloads so the extra code can be DCEd when people call the function with no flags. --- doc/changelog.dox | 4 +- src/Magnum/Primitives/Plane.cpp | 110 ++++++++++++++++------- src/Magnum/Primitives/Plane.h | 24 +++-- src/Magnum/Primitives/Test/PlaneTest.cpp | 62 ++++++++----- 4 files changed, 142 insertions(+), 58 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index c9f746158..b851a0ada 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -196,8 +196,8 @@ See also: - @ref Primitives::capsule3DSolid(), @ref Primitives::circle3DSolid(), @ref Primitives::coneSolid(), @ref Primitives::cylinderSolid(), - @ref Primitives::grid3DSolid() and @ref Primitives::uvSphereSolid() can now - have tangents as well + @ref Primitives::grid3DSolid(), @ref Primitives::planeSolid() and + @ref Primitives::uvSphereSolid() can now have tangents as well @subsubsection changelog-latest-new-scenegraph SceneGraph library diff --git a/src/Magnum/Primitives/Plane.cpp b/src/Magnum/Primitives/Plane.cpp index cf70fcb3d..0cee59196 100644 --- a/src/Magnum/Primitives/Plane.cpp +++ b/src/Magnum/Primitives/Plane.cpp @@ -26,7 +26,7 @@ #include "Plane.h" #include "Magnum/Mesh.h" -#include "Magnum/Math/Vector3.h" +#include "Magnum/Math/Vector4.h" #include "Magnum/Trade/MeshData.h" namespace Magnum { namespace Primitives { @@ -42,16 +42,6 @@ constexpr struct VertexSolid { {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, {{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}} }; -constexpr struct VertexSolidTextureCoords { - Vector3 position; - Vector3 normal; - Vector2 textureCoords; -} VerticesSolidTextureCoords[] { - {VerticesSolid[0].position, VerticesSolid[0].normal, {1.0f, 0.0f}}, - {VerticesSolid[1].position, VerticesSolid[1].normal, {1.0f, 1.0f}}, - {VerticesSolid[2].position, VerticesSolid[2].normal, {0.0f, 0.0f}}, - {VerticesSolid[3].position, VerticesSolid[3].normal, {0.0f, 1.0f}} -}; constexpr Trade::MeshAttributeData AttributesSolid[]{ Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].position, @@ -60,34 +50,92 @@ constexpr Trade::MeshAttributeData AttributesSolid[]{ Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].normal, Containers::arraySize(VerticesSolid), sizeof(VertexSolid))} }; -constexpr Trade::MeshAttributeData AttributesSolidTextureCoords[]{ - Trade::MeshAttributeData{Trade::MeshAttribute::Position, - Containers::stridedArrayView(VerticesSolidTextureCoords, - &VerticesSolidTextureCoords[0].position, - Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))}, - Trade::MeshAttributeData{Trade::MeshAttribute::Normal, - Containers::stridedArrayView(VerticesSolidTextureCoords, - &VerticesSolidTextureCoords[0].normal, - Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))}, - Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, - Containers::stridedArrayView(VerticesSolidTextureCoords, - &VerticesSolidTextureCoords[0].textureCoords, - Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))}, -}; } -Trade::MeshData planeSolid(const PlaneFlags flags) { - if(flags & PlaneFlag::TextureCoordinates) - return Trade::MeshData{MeshPrimitive::TriangleStrip, - {}, VerticesSolidTextureCoords, - Trade::meshAttributeDataNonOwningArray(AttributesSolidTextureCoords)}; - +Trade::MeshData planeSolid() { return Trade::MeshData{MeshPrimitive::TriangleStrip, {}, VerticesSolid, Trade::meshAttributeDataNonOwningArray(AttributesSolid)}; } +Trade::MeshData planeSolid(const PlaneFlags flags) { + /* Return the compile-time data if nothing extra is requested */ + if(!flags) return planeSolid(); + + /* Calculate attribute count and vertex size */ + std::size_t stride = sizeof(Vector3) + sizeof(Vector3); + std::size_t attributeCount = 2; + if(flags & PlaneFlag::Tangents) { + stride += sizeof(Vector4); + ++attributeCount; + } + if(flags & PlaneFlag::TextureCoordinates) { + stride += sizeof(Vector2); + ++attributeCount; + } + + /* Set up the layout */ + Containers::Array vertexData{Containers::NoInit, 4*stride}; + Containers::Array attributeData{attributeCount}; + std::size_t attributeIndex = 0; + std::size_t attributeOffset = 0; + + Containers::StridedArrayView1D positions{vertexData, + reinterpret_cast(vertexData.data() + attributeOffset), + 4, std::ptrdiff_t(stride)}; + attributeData[attributeIndex++] = Trade::MeshAttributeData{ + Trade::MeshAttribute::Position, positions}; + attributeOffset += sizeof(Vector3); + + Containers::StridedArrayView1D normals{vertexData, + reinterpret_cast(vertexData.data() + sizeof(Vector3)), + 4, std::ptrdiff_t(stride)}; + attributeData[attributeIndex++] = Trade::MeshAttributeData{ + Trade::MeshAttribute::Normal, normals}; + attributeOffset += sizeof(Vector3); + + Containers::StridedArrayView1D tangents; + if(flags & PlaneFlag::Tangents) { + tangents = Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + attributeOffset), + 4, std::ptrdiff_t(stride)}; + attributeData[attributeIndex++] = Trade::MeshAttributeData{ + Trade::MeshAttribute::Tangent, tangents}; + attributeOffset += sizeof(Vector4); + } + + Containers::StridedArrayView1D textureCoordinates; + if(flags & PlaneFlag::TextureCoordinates) { + textureCoordinates = Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + attributeOffset), + 4, std::ptrdiff_t(stride)}; + attributeData[attributeIndex++] = Trade::MeshAttributeData{ + Trade::MeshAttribute::TextureCoordinates, textureCoordinates}; + attributeOffset += sizeof(Vector2); + } + + CORRADE_INTERNAL_ASSERT(attributeIndex == attributeCount); + CORRADE_INTERNAL_ASSERT(attributeOffset == stride); + + /* Fill the data */ + for(std::size_t i = 0; i != 4; ++i) { + positions[i] = VerticesSolid[i].position; + normals[i] = VerticesSolid[i].normal; + if(flags & PlaneFlag::Tangents) + tangents[i] = {1.0f, 0.0f, 0.0f, 1.0f}; + } + if(flags & PlaneFlag::TextureCoordinates) { + textureCoordinates[0] = {1.0f, 0.0f}; + textureCoordinates[1] = {1.0f, 1.0f}; + textureCoordinates[2] = {0.0f, 0.0f}; + textureCoordinates[3] = {0.0f, 1.0f}; + } + + return Trade::MeshData{MeshPrimitive::TriangleStrip, + std::move(vertexData), std::move(attributeData)}; +} + #ifdef MAGNUM_BUILD_DEPRECATED CORRADE_IGNORE_DEPRECATED_PUSH Trade::MeshData planeSolid(const PlaneTextureCoords textureCoords) { diff --git a/src/Magnum/Primitives/Plane.h b/src/Magnum/Primitives/Plane.h index baabffb15..85dedae51 100644 --- a/src/Magnum/Primitives/Plane.h +++ b/src/Magnum/Primitives/Plane.h @@ -45,7 +45,15 @@ namespace Magnum { namespace Primitives { */ enum class PlaneFlag: UnsignedByte { /** Generate texture coordinates with origin in bottom left corner */ - TextureCoordinates = 1 << 0 + TextureCoordinates = 1 << 0, + + /** + * Generate four-component tangents. The last component can be used to + * reconstruct a bitangent as described in the documentation of + * @ref Trade::MeshAttribute::Tangent. + * @m_since_latest + */ + Tangents = 1 << 1 }; /** @@ -77,16 +85,22 @@ enum class CORRADE_DEPRECATED_ENUM("use PlaneFlags instead") PlaneTextureCoords: @m_since_latest 2x2 plane. Non-indexed @ref MeshPrimitive::TriangleStrip on the XY plane with -@ref VertexFormat::Vector3 positions and @ref VertexFormat::Vector3 normals in -positive Z direction. The returned instance references data stored in constant -memory. +@ref VertexFormat::Vector3 positions, @ref VertexFormat::Vector3 normals in +positive Z direction, optional @ref VertexFormat::Vector4 tangents and optional +@ref VertexFormat::Vector2 texture coordinates. The returned instance may +reference data stored in constant memory. @image html primitives-planesolid.png width=256px @see @ref planeWireframe(), @ref squareSolid(), @ref gradient3D(), @ref MeshTools::generateTriangleStripIndices() */ -MAGNUM_PRIMITIVES_EXPORT Trade::MeshData planeSolid(PlaneFlags flags = {}); +MAGNUM_PRIMITIVES_EXPORT Trade::MeshData planeSolid(PlaneFlags flags); + +/** @overload */ +/* Separate API so apps that don't need texture coordinate / tangents don't + need to drag in the extra code needed to allocate & calculate them */ +MAGNUM_PRIMITIVES_EXPORT Trade::MeshData planeSolid(); #ifdef MAGNUM_BUILD_DEPRECATED /** diff --git a/src/Magnum/Primitives/Test/PlaneTest.cpp b/src/Magnum/Primitives/Test/PlaneTest.cpp index 527e63f37..f66d65024 100644 --- a/src/Magnum/Primitives/Test/PlaneTest.cpp +++ b/src/Magnum/Primitives/Test/PlaneTest.cpp @@ -26,7 +26,7 @@ #include #include "Magnum/Mesh.h" -#include "Magnum/Math/Vector3.h" +#include "Magnum/Math/Vector4.h" #include "Magnum/Primitives/Plane.h" #include "Magnum/Trade/MeshData.h" @@ -36,42 +36,64 @@ struct PlaneTest: TestSuite::Tester { explicit PlaneTest(); void solid(); - void solidTextured(); void wireframe(); }; +constexpr struct { + const char* name; + PlaneFlags flags; +} SolidData[] { + {"", PlaneFlags{}}, + {"texture coordinates", PlaneFlag::TextureCoordinates}, + {"tangents", PlaneFlag::Tangents}, + {"both", PlaneFlag::TextureCoordinates|PlaneFlag::Tangents} +}; + PlaneTest::PlaneTest() { - addTests({&PlaneTest::solid, - &PlaneTest::solidTextured, - &PlaneTest::wireframe}); + addInstancedTests({&PlaneTest::solid}, + Containers::arraySize(SolidData)); + + addTests({&PlaneTest::wireframe}); } void PlaneTest::solid() { - Trade::MeshData plane = Primitives::planeSolid(); + auto&& data = SolidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Trade::MeshData plane = Primitives::planeSolid(data.flags); CORRADE_COMPARE(plane.primitive(), MeshPrimitive::TriangleStrip); CORRADE_VERIFY(!plane.isIndexed()); CORRADE_COMPARE(plane.vertexCount(), 4); - CORRADE_COMPARE(plane.attributeCount(), 2); + CORRADE_COMPARE(plane.attribute(Trade::MeshAttribute::Position)[3], (Vector3{-1.0f, 1.0f, 0.0f})); - CORRADE_COMPARE(plane.attribute(Trade::MeshAttribute::Normal)[2], - (Vector3{0.0f, 0.0f, 1.0f})); -} -void PlaneTest::solidTextured() { - Trade::MeshData plane = Primitives::planeSolid(Primitives::PlaneFlag::TextureCoordinates); + if(data.flags & PlaneFlag::Tangents) + CORRADE_COMPARE(plane.attribute(Trade::MeshAttribute::Tangent)[1], + (Vector4{1.0f, 0.0f, 0.0f, 1.0f})); + else CORRADE_VERIFY(!plane.hasAttribute(Trade::MeshAttribute::Tangent)); - CORRADE_COMPARE(plane.primitive(), MeshPrimitive::TriangleStrip); - CORRADE_VERIFY(!plane.isIndexed()); - CORRADE_COMPARE(plane.vertexCount(), 4); - CORRADE_COMPARE(plane.attributeCount(), 3); - CORRADE_COMPARE(plane.attribute(Trade::MeshAttribute::Position)[3], - (Vector3{-1.0f, 1.0f, 0.0f})); CORRADE_COMPARE(plane.attribute(Trade::MeshAttribute::Normal)[2], (Vector3{0.0f, 0.0f, 1.0f})); - CORRADE_COMPARE(plane.attribute(Trade::MeshAttribute::TextureCoordinates)[1], - (Vector2{1.0f, 1.0f})); + + if(data.flags & PlaneFlag::TextureCoordinates) + CORRADE_COMPARE(plane.attribute(Trade::MeshAttribute::TextureCoordinates)[1], + (Vector2{1.0f, 1.0f})); + else CORRADE_VERIFY(!plane.hasAttribute(Trade::MeshAttribute::TextureCoordinates)); + + if(data.flags & PlaneFlag::Tangents) { + auto tangents = plane.attribute(Trade::MeshAttribute::Tangent); + auto normals = plane.attribute(Trade::MeshAttribute::Normal); + for(std::size_t i = 0; i != tangents.size(); ++i) { + CORRADE_ITERATION(i); + CORRADE_ITERATION(tangents[i]); + CORRADE_ITERATION(normals[i]); + CORRADE_VERIFY(tangents[i].xyz().isNormalized()); + CORRADE_VERIFY(normals[i].isNormalized()); + CORRADE_COMPARE(Math::dot(tangents[i].xyz(), normals[i]), 0.0f); + } + } } void PlaneTest::wireframe() {