Browse Source

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.
pull/430/head
Vladimír Vondruš 6 years ago
parent
commit
2a88a885c1
  1. 4
      doc/changelog.dox
  2. 110
      src/Magnum/Primitives/Plane.cpp
  3. 24
      src/Magnum/Primitives/Plane.h
  4. 58
      src/Magnum/Primitives/Test/PlaneTest.cpp

4
doc/changelog.dox

@ -196,8 +196,8 @@ See also:
- @ref Primitives::capsule3DSolid(), @ref Primitives::circle3DSolid(), - @ref Primitives::capsule3DSolid(), @ref Primitives::circle3DSolid(),
@ref Primitives::coneSolid(), @ref Primitives::cylinderSolid(), @ref Primitives::coneSolid(), @ref Primitives::cylinderSolid(),
@ref Primitives::grid3DSolid() and @ref Primitives::uvSphereSolid() can now @ref Primitives::grid3DSolid(), @ref Primitives::planeSolid() and
have tangents as well @ref Primitives::uvSphereSolid() can now have tangents as well
@subsubsection changelog-latest-new-scenegraph SceneGraph library @subsubsection changelog-latest-new-scenegraph SceneGraph library

110
src/Magnum/Primitives/Plane.cpp

@ -26,7 +26,7 @@
#include "Plane.h" #include "Plane.h"
#include "Magnum/Mesh.h" #include "Magnum/Mesh.h"
#include "Magnum/Math/Vector3.h" #include "Magnum/Math/Vector4.h"
#include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives { 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}},
{{-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[]{ constexpr Trade::MeshAttributeData AttributesSolid[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].position, Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].position,
@ -60,34 +50,92 @@ constexpr Trade::MeshAttributeData AttributesSolid[]{
Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].normal, Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].normal,
Containers::arraySize(VerticesSolid), sizeof(VertexSolid))} 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) { Trade::MeshData planeSolid() {
if(flags & PlaneFlag::TextureCoordinates)
return Trade::MeshData{MeshPrimitive::TriangleStrip,
{}, VerticesSolidTextureCoords,
Trade::meshAttributeDataNonOwningArray(AttributesSolidTextureCoords)};
return Trade::MeshData{MeshPrimitive::TriangleStrip, return Trade::MeshData{MeshPrimitive::TriangleStrip,
{}, VerticesSolid, {}, VerticesSolid,
Trade::meshAttributeDataNonOwningArray(AttributesSolid)}; 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<char> vertexData{Containers::NoInit, 4*stride};
Containers::Array<Trade::MeshAttributeData> attributeData{attributeCount};
std::size_t attributeIndex = 0;
std::size_t attributeOffset = 0;
Containers::StridedArrayView1D<Vector3> positions{vertexData,
reinterpret_cast<Vector3*>(vertexData.data() + attributeOffset),
4, std::ptrdiff_t(stride)};
attributeData[attributeIndex++] = Trade::MeshAttributeData{
Trade::MeshAttribute::Position, positions};
attributeOffset += sizeof(Vector3);
Containers::StridedArrayView1D<Vector3> normals{vertexData,
reinterpret_cast<Vector3*>(vertexData.data() + sizeof(Vector3)),
4, std::ptrdiff_t(stride)};
attributeData[attributeIndex++] = Trade::MeshAttributeData{
Trade::MeshAttribute::Normal, normals};
attributeOffset += sizeof(Vector3);
Containers::StridedArrayView1D<Vector4> tangents;
if(flags & PlaneFlag::Tangents) {
tangents = Containers::StridedArrayView1D<Vector4>{vertexData,
reinterpret_cast<Vector4*>(vertexData.data() + attributeOffset),
4, std::ptrdiff_t(stride)};
attributeData[attributeIndex++] = Trade::MeshAttributeData{
Trade::MeshAttribute::Tangent, tangents};
attributeOffset += sizeof(Vector4);
}
Containers::StridedArrayView1D<Vector2> textureCoordinates;
if(flags & PlaneFlag::TextureCoordinates) {
textureCoordinates = Containers::StridedArrayView1D<Vector2>{vertexData,
reinterpret_cast<Vector2*>(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 #ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH
Trade::MeshData planeSolid(const PlaneTextureCoords textureCoords) { Trade::MeshData planeSolid(const PlaneTextureCoords textureCoords) {

24
src/Magnum/Primitives/Plane.h

@ -45,7 +45,15 @@ namespace Magnum { namespace Primitives {
*/ */
enum class PlaneFlag: UnsignedByte { enum class PlaneFlag: UnsignedByte {
/** Generate texture coordinates with origin in bottom left corner */ /** 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 @m_since_latest
2x2 plane. Non-indexed @ref MeshPrimitive::TriangleStrip on the XY plane with 2x2 plane. Non-indexed @ref MeshPrimitive::TriangleStrip on the XY plane with
@ref VertexFormat::Vector3 positions and @ref VertexFormat::Vector3 normals in @ref VertexFormat::Vector3 positions, @ref VertexFormat::Vector3 normals in
positive Z direction. The returned instance references data stored in constant positive Z direction, optional @ref VertexFormat::Vector4 tangents and optional
memory. @ref VertexFormat::Vector2 texture coordinates. The returned instance may
reference data stored in constant memory.
@image html primitives-planesolid.png width=256px @image html primitives-planesolid.png width=256px
@see @ref planeWireframe(), @ref squareSolid(), @ref gradient3D(), @see @ref planeWireframe(), @ref squareSolid(), @ref gradient3D(),
@ref MeshTools::generateTriangleStripIndices() @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 #ifdef MAGNUM_BUILD_DEPRECATED
/** /**

58
src/Magnum/Primitives/Test/PlaneTest.cpp

@ -26,7 +26,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/Mesh.h" #include "Magnum/Mesh.h"
#include "Magnum/Math/Vector3.h" #include "Magnum/Math/Vector4.h"
#include "Magnum/Primitives/Plane.h" #include "Magnum/Primitives/Plane.h"
#include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/MeshData.h"
@ -36,42 +36,64 @@ struct PlaneTest: TestSuite::Tester {
explicit PlaneTest(); explicit PlaneTest();
void solid(); void solid();
void solidTextured();
void wireframe(); 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() { PlaneTest::PlaneTest() {
addTests({&PlaneTest::solid, addInstancedTests({&PlaneTest::solid},
&PlaneTest::solidTextured, Containers::arraySize(SolidData));
&PlaneTest::wireframe});
addTests({&PlaneTest::wireframe});
} }
void PlaneTest::solid() { 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_COMPARE(plane.primitive(), MeshPrimitive::TriangleStrip);
CORRADE_VERIFY(!plane.isIndexed()); CORRADE_VERIFY(!plane.isIndexed());
CORRADE_COMPARE(plane.vertexCount(), 4); CORRADE_COMPARE(plane.vertexCount(), 4);
CORRADE_COMPARE(plane.attributeCount(), 2);
CORRADE_COMPARE(plane.attribute<Vector3>(Trade::MeshAttribute::Position)[3], CORRADE_COMPARE(plane.attribute<Vector3>(Trade::MeshAttribute::Position)[3],
(Vector3{-1.0f, 1.0f, 0.0f})); (Vector3{-1.0f, 1.0f, 0.0f}));
CORRADE_COMPARE(plane.attribute<Vector3>(Trade::MeshAttribute::Normal)[2],
(Vector3{0.0f, 0.0f, 1.0f}));
}
void PlaneTest::solidTextured() { if(data.flags & PlaneFlag::Tangents)
Trade::MeshData plane = Primitives::planeSolid(Primitives::PlaneFlag::TextureCoordinates); CORRADE_COMPARE(plane.attribute<Vector4>(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<Vector3>(Trade::MeshAttribute::Position)[3],
(Vector3{-1.0f, 1.0f, 0.0f}));
CORRADE_COMPARE(plane.attribute<Vector3>(Trade::MeshAttribute::Normal)[2], CORRADE_COMPARE(plane.attribute<Vector3>(Trade::MeshAttribute::Normal)[2],
(Vector3{0.0f, 0.0f, 1.0f})); (Vector3{0.0f, 0.0f, 1.0f}));
if(data.flags & PlaneFlag::TextureCoordinates)
CORRADE_COMPARE(plane.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates)[1], CORRADE_COMPARE(plane.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates)[1],
(Vector2{1.0f, 1.0f})); (Vector2{1.0f, 1.0f}));
else CORRADE_VERIFY(!plane.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags & PlaneFlag::Tangents) {
auto tangents = plane.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = plane.attribute<Vector3>(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() { void PlaneTest::wireframe() {

Loading…
Cancel
Save