Browse Source

Primitives: tangents in the 3D grid primitive.

pull/430/head
Vladimír Vondruš 6 years ago
parent
commit
b16861693a
  1. 5
      doc/changelog.dox
  2. 24
      src/Magnum/Primitives/Grid.cpp
  3. 14
      src/Magnum/Primitives/Grid.h
  4. 337
      src/Magnum/Primitives/Test/GridTest.cpp

5
doc/changelog.dox

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

24
src/Magnum/Primitives/Grid.cpp

@ -56,16 +56,20 @@ Trade::MeshData grid3DSolid(const Vector2i& subdivisions, const GridFlags flags)
}
}
/* Allocate interleaved array for all vertex data */
/* Calculate attribute count and vertex size */
std::size_t stride = sizeof(Vector3);
std::size_t attributeCount = 1;
if(flags & GridFlag::Normals) {
++attributeCount;
stride += sizeof(Vector3);
++attributeCount;
}
if(flags & GridFlag::TextureCoordinates) {
if(flags & GridFlag::Tangents) {
stride += sizeof(Vector4);
++attributeCount;
}
if(flags & GridFlag::TextureCoordinates) {
stride += sizeof(Vector2);
++attributeCount;
}
Containers::Array<char> vertexData{stride*vertexCount.product()};
Containers::Array<Trade::MeshAttributeData> attributes{attributeCount};
@ -86,8 +90,7 @@ Trade::MeshData grid3DSolid(const Vector2i& subdivisions, const GridFlags flags)
positions[i++] = {(Vector2(x, y)/Vector2(faceCount))*2.0f - Vector2{1.0f}, 0.0f};
}
/* Fill normals, if any. It's always the second attribute, right after
positions. */
/* Fill normals and tangents, if any. Those are the same for all. */
if(flags & GridFlag::Normals) {
Containers::StridedArrayView1D<Vector3> normals{vertexData,
reinterpret_cast<Vector3*>(vertexData.begin() + attributeOffset),
@ -97,6 +100,15 @@ Trade::MeshData grid3DSolid(const Vector2i& subdivisions, const GridFlags flags)
attributeOffset += sizeof(Vector3);
for(auto&& i: normals) i = Vector3::zAxis(1.0f);
}
if(flags & GridFlag::Tangents) {
Containers::StridedArrayView1D<Vector4> tangents{vertexData,
reinterpret_cast<Vector4*>(vertexData.begin() + attributeOffset),
std::size_t(vertexCount.product()), std::ptrdiff_t(stride)};
attributes[attributeIndex++] =
Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, tangents};
attributeOffset += sizeof(Vector4);
for(auto&& i: tangents) i = {1.0f, 0.0f, 0.0f, 1.0f};
}
if(flags & GridFlag::TextureCoordinates) {
Containers::StridedArrayView1D<Vector2> textureCoords{vertexData,
@ -109,8 +121,6 @@ Trade::MeshData grid3DSolid(const Vector2i& subdivisions, const GridFlags flags)
textureCoords[i] = positions[i].xy()*0.5f + Vector2{0.5f};
}
/* Not using a compile-time attribute array because there's way too many
combinations */
return Trade::MeshData{MeshPrimitive::Triangles,
std::move(indexData), Trade::MeshIndexData{indices},
std::move(vertexData), std::move(attributes)};

14
src/Magnum/Primitives/Grid.h

@ -70,8 +70,16 @@ enum class GridFlag: UnsignedByte {
* Generate normals in positive Z direction.
* @m_deprecated_since_latest Use @ref GridFlag::Normals instead.
*/
GenerateNormals CORRADE_DEPRECATED_ENUM("use Normals instead") = Normals
GenerateNormals CORRADE_DEPRECATED_ENUM("use Normals instead") = Normals,
#endif
/**
* 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 << 2
};
/**
@ -89,8 +97,8 @@ CORRADE_ENUMSET_OPERATORS(GridFlags)
2x2 grid in the XY plane with normals in positive Z direction.
@ref MeshPrimitive::Triangles with @ref MeshIndexType::UnsignedInt indices,
interleaved @ref VertexFormat::Vector3 positions, optional
@ref VertexFormat::Vector3 normals and @ref VertexFormat::Vector2 texture
coordinates.
@ref VertexFormat::Vector3 normals, optional @ref VertexFormat::Vector4
tangents and optional @ref VertexFormat::Vector2 texture coordinates.
@image html primitives-grid3dsolid.png width=256px

337
src/Magnum/Primitives/Test/GridTest.cpp

@ -26,7 +26,7 @@
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/Vector3.h"
#include "Magnum/Math/Vector4.h"
#include "Magnum/Primitives/Grid.h"
#include "Magnum/Trade/MeshData.h"
@ -35,103 +35,37 @@ namespace Magnum { namespace Primitives { namespace Test { namespace {
struct GridTest: TestSuite::Tester {
explicit GridTest();
void solid3DWithoutAnything();
void solid3DWithNormalsAndTextureCoords();
void solid3D();
void wireframe3D();
};
GridTest::GridTest() {
addTests({&GridTest::solid3DWithoutAnything,
&GridTest::solid3DWithNormalsAndTextureCoords,
&GridTest::wireframe3D});
}
void GridTest::solid3DWithoutAnything() {
Trade::MeshData grid = grid3DSolid({5, 3}, {});
CORRADE_COMPARE(grid.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(grid.isIndexed());
CORRADE_COMPARE(grid.attributeCount(), 1);
CORRADE_COMPARE_AS(grid.attribute<Vector3>(Trade::MeshAttribute::Position), Containers::arrayView<Vector3>({
{-1.0f, -1.0f, 0.0f},
{-0.666667f, -1.0f, 0.0f},
{-0.333333f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.333333f, -1.0f, 0.0f},
{0.666667f, -1.0f, 0.0f},
{1.0f, -1.0f, 0.0f},
{-1.0f, -0.5f, 0.0f},
{-0.666667f, -0.5f, 0.0f},
{-0.333333f, -0.5f, 0.0f},
{0.0f, -0.5f, 0.0f},
{0.333333f, -0.5f, 0.0f},
{0.666667f, -0.5f, 0.0f},
{1.0f, -0.5f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-0.666667f, 0.0f, 0.0f},
{-0.333333f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f},
{0.333333f, 0.0f, 0.0f},
{0.666667f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{-1.0f, 0.5f, 0.0f},
{-0.666667f, 0.5f, 0.0f},
{-0.333333f, 0.5f, 0.0f},
{0.0f, 0.5f, 0.0f},
{0.333333f, 0.5f, 0.0f},
{0.666667f, 0.5f, 0.0f},
{1.0f, 0.5f, 0.0f},
{-1.0f, 1.0f, 0.0f},
{-0.666667f, 1.0f, 0.0f},
{-0.333333f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.333333f, 1.0f, 0.0f},
{0.666667f, 1.0f, 0.0f},
{1.0f, 1.0f, 0.0f}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(grid.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
0, 8, 7, 0, 1, 8,
1, 9, 8, 1, 2, 9,
2, 10, 9, 2, 3, 10,
3, 11, 10, 3, 4, 11,
4, 12, 11, 4, 5, 12,
5, 13, 12, 5, 6, 13,
7, 15, 14, 7, 8, 15,
8, 16, 15, 8, 9, 16,
9, 17, 16, 9, 10, 17,
10, 18, 17, 10, 11, 18,
11, 19, 18, 11, 12, 19,
12, 20, 19, 12, 13, 20,
constexpr struct {
const char* name;
GridFlags flags;
} Solid3DData[] {
{"", GridFlags{}},
{"normals", GridFlag::Normals},
{"texture coordinates", GridFlag::TextureCoordinates},
{"tangents", GridFlag::Tangents},
{"normals + tangents", GridFlag::Normals|GridFlag::Tangents},
{"all", GridFlag::TextureCoordinates|GridFlag::Normals|GridFlag::Tangents}
};
14, 22, 21, 14, 15, 22,
15, 23, 22, 15, 16, 23,
16, 24, 23, 16, 17, 24,
17, 25, 24, 17, 18, 25,
18, 26, 25, 18, 19, 26,
19, 27, 26, 19, 20, 27,
GridTest::GridTest() {
addInstancedTests({&GridTest::solid3D},
Containers::arraySize(Solid3DData));
21, 29, 28, 21, 22, 29,
22, 30, 29, 22, 23, 30,
23, 31, 30, 23, 24, 31,
24, 32, 31, 24, 25, 32,
25, 33, 32, 25, 26, 33,
26, 34, 33, 26, 27, 34
}), TestSuite::Compare::Container);
addTests({&GridTest::wireframe3D});
}
void GridTest::solid3DWithNormalsAndTextureCoords() {
Trade::MeshData grid = grid3DSolid({5, 3}, GridFlag::Normals|GridFlag::TextureCoordinates);
void GridTest::solid3D() {
auto&& data = Solid3DData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData grid = grid3DSolid({5, 3}, data.flags);
CORRADE_COMPARE(grid.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(grid.isIndexed());
CORRADE_COMPARE(grid.attributeCount(), 3);
CORRADE_COMPARE_AS(grid.attribute<Vector3>(Trade::MeshAttribute::Position), Containers::arrayView<Vector3>({
{-1.0f, -1.0f, 0.0f},
@ -175,89 +109,150 @@ void GridTest::solid3DWithNormalsAndTextureCoords() {
{1.0f, 1.0f, 0.0f}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(grid.attribute<Vector3>(Trade::MeshAttribute::Normal), Containers::arrayView<Vector3>({
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(grid.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.0f, 0.0f},
{0.166667f, 0.0f},
{0.333333f, 0.0f},
{0.5f, 0.0f},
{0.666667f, 0.0f},
{0.833333f, 0.0f},
{1.0f, 0.0f},
{0.0f, 0.25f},
{0.166667f, 0.25f},
{0.333333f, 0.25f},
{0.5f, 0.25f},
{0.666667f, 0.25f},
{0.833333f, 0.25f},
{1.0f, 0.25f},
{0.0f, 0.5f},
{0.166667f, 0.5f},
{0.333333f, 0.5f},
{0.5f, 0.5f},
{0.666667f, 0.5f},
{0.833333f, 0.5f},
{1.0f, 0.5f},
{0.0f, 0.75f},
{0.166667f, 0.75f},
{0.333333f, 0.75f},
{0.5f, 0.75f},
{0.666667f, 0.75f},
{0.833333f, 0.75f},
{1.0f, 0.75f},
{0.0f, 1.0f},
{0.166667f, 1.0f},
{0.333333f, 1.0f},
{0.5f, 1.0f},
{0.666667f, 1.0f},
{0.833333f, 1.0f},
{1.0f, 1.0f}
}), TestSuite::Compare::Container);
if(data.flags & GridFlag::Tangents) {
CORRADE_COMPARE_AS(grid.attribute<Vector4>(Trade::MeshAttribute::Tangent), Containers::arrayView<Vector4>({
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!grid.hasAttribute(Trade::MeshAttribute::Tangent));
if(data.flags & GridFlag::Normals) {
CORRADE_COMPARE_AS(grid.attribute<Vector3>(Trade::MeshAttribute::Normal), Containers::arrayView<Vector3>({
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!grid.hasAttribute(Trade::MeshAttribute::Normal));
if(data.flags & GridFlag::TextureCoordinates) {
CORRADE_COMPARE_AS(grid.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.0f, 0.0f},
{0.166667f, 0.0f},
{0.333333f, 0.0f},
{0.5f, 0.0f},
{0.666667f, 0.0f},
{0.833333f, 0.0f},
{1.0f, 0.0f},
{0.0f, 0.25f},
{0.166667f, 0.25f},
{0.333333f, 0.25f},
{0.5f, 0.25f},
{0.666667f, 0.25f},
{0.833333f, 0.25f},
{1.0f, 0.25f},
{0.0f, 0.5f},
{0.166667f, 0.5f},
{0.333333f, 0.5f},
{0.5f, 0.5f},
{0.666667f, 0.5f},
{0.833333f, 0.5f},
{1.0f, 0.5f},
{0.0f, 0.75f},
{0.166667f, 0.75f},
{0.333333f, 0.75f},
{0.5f, 0.75f},
{0.666667f, 0.75f},
{0.833333f, 0.75f},
{1.0f, 0.75f},
{0.0f, 1.0f},
{0.166667f, 1.0f},
{0.333333f, 1.0f},
{0.5f, 1.0f},
{0.666667f, 1.0f},
{0.833333f, 1.0f},
{1.0f, 1.0f}
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!grid.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags >= (GridFlag::Tangents|GridFlag::Normals)) {
auto tangents = grid.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = grid.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);
}
}
CORRADE_COMPARE_AS(grid.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
0, 8, 7, 0, 1, 8,

Loading…
Cancel
Save