mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
613 lines
23 KiB
613 lines
23 KiB
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
|
2020, 2021, 2022, 2023, 2024, 2025, 2026 |
|
Vladimír Vondruš <mosra@centrum.cz> |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a |
|
copy of this software and associated documentation files (the "Software"), |
|
to deal in the Software without restriction, including without limitation |
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
and/or sell copies of the Software, and to permit persons to whom the |
|
Software is furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included |
|
in all copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
#include <Corrade/Containers/Optional.h> |
|
#include <Corrade/Containers/String.h> |
|
#include <Corrade/TestSuite/Tester.h> |
|
#include <Corrade/TestSuite/Compare/String.h> |
|
|
|
#include "Magnum/Mesh.h" |
|
#include "Magnum/Math/Functions.h" |
|
#include "Magnum/Math/FunctionsBatch.h" /* minmax() */ |
|
#include "Magnum/Math/Vector4.h" |
|
#include "Magnum/Primitives/Cube.h" |
|
#include "Magnum/Trade/MeshData.h" |
|
|
|
namespace Magnum { namespace Primitives { namespace Test { namespace { |
|
|
|
struct CubeTest: TestSuite::Tester { |
|
explicit CubeTest(); |
|
|
|
void solid(); |
|
template<CubeFlag flag = CubeFlag{}> void solidTextureCoordinates(); |
|
void solidInvalid(); |
|
void solidStrip(); |
|
void solidStripGlsl(); |
|
void wireframe(); |
|
}; |
|
|
|
const struct { |
|
const char* name; |
|
Containers::Optional<CubeFlags> flags; |
|
} SolidData[]{ |
|
{"", {}}, |
|
{"explicit empty flags", CubeFlags{}} |
|
}; |
|
|
|
enum class CubeEdge { |
|
/* 0 is reserved */ |
|
|
|
/* Horizontal edges */ |
|
BottomBack = 1, /* {0, -1, +1} */ |
|
BottomFront, /* {0, -1, -1} */ |
|
TopBack, /* {0, +1, +1} */ |
|
TopFront, /* {0, +1, -1} */ |
|
|
|
/* Vertical edges */ |
|
BackLeft, /* {-1, 0, +1} */ |
|
BackRight, /* {+1, 0, +1} */ |
|
FrontLeft, /* {-1, 0, -1} */ |
|
FrontRight, /* {+1, 0, -1} */ |
|
|
|
/* "Depth" edges */ |
|
BottomLeft, /* {-1, -1, 0} */ |
|
BottomRight, /* {+1, -1, 0} */ |
|
TopLeft, /* {-1, +1, 0} */ |
|
TopRight, /* {+1, +1, 0} */ |
|
}; |
|
|
|
Debug& operator<<(Debug& out, CubeEdge edge) { |
|
switch(edge) { |
|
#define _c(value) case CubeEdge::value: return out << #value; |
|
_c(BottomBack) |
|
_c(BottomFront) |
|
_c(TopBack) |
|
_c(TopFront) |
|
_c(BackLeft) |
|
_c(BackRight) |
|
_c(FrontLeft) |
|
_c(FrontRight) |
|
_c(BottomLeft) |
|
_c(BottomRight) |
|
_c(TopLeft) |
|
_c(TopRight) |
|
#undef _c |
|
} |
|
|
|
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); |
|
} |
|
|
|
const struct { |
|
const char* name; |
|
CubeFlags flags; |
|
/* +X, -X, +Y, -Y, +Z, -Z (same order as GL::CubeMapCoordinate) */ |
|
Vector2 expectedCenters[6]; |
|
/* Cases where less than 12 edges are shared have the rest zero-filled */ |
|
CubeEdge expectedSharedEdges[12]; |
|
} SolidTextureCoordinatesData[]{ |
|
{"all same", CubeFlag::TextureCoordinatesAllSame, { |
|
{0.5f, 0.5f}, |
|
{0.5f, 0.5f}, |
|
{0.5f, 0.5f}, |
|
{0.5f, 0.5f}, |
|
{0.5f, 0.5f}, |
|
{0.5f, 0.5f} |
|
}, { |
|
/* (No shared edges in this case) */ |
|
}}, |
|
/* +----+----+----+ 1.0 |
|
| +X | +Y | +Z | 0.75 |
|
+----+----+----+ 0.5 |
|
| -X | -Y | -Z | 0.25 |
|
+----+----+----+ 0.0 |
|
0.0 0.333 0.667 1.0 |
|
0.167 0.5 0.833 */ |
|
{"+ up, - down", CubeFlag::TextureCoordinatesPositiveUpNegativeDown, { |
|
{0.16667f, 0.75f}, /* +X */ |
|
{0.16667f, 0.25f}, /* -X */ |
|
{0.5f, 0.75f}, /* +Y */ |
|
{0.5f, 0.25f}, /* -Y */ |
|
{0.83333f, 0.75f}, /* +Z */ |
|
{0.83333f, 0.25f} /* -Z */ |
|
}, { |
|
/* (*Deliberately* no shared edges in this case either. They could be |
|
but it'd mean some faces would be rotated, which is just weird. */ |
|
}}, |
|
/* +-----+ 1.0 |
|
| +Y | 0.833 |
|
+-tl--+-----+-----+-----+ 0.667 |
|
| -X bl +Z br +X fr -Z | 0.5 |
|
+-bl--+-----+-----+-----+ 0.333 |
|
| -Y | 0.167 |
|
+-----+ 0.0 |
|
0.0 0.25 0.5 0.75 1.0 |
|
0.125 0.375 0.625 0.875 */ |
|
{"-X up, -X down", CubeFlag::TextureCoordinatesNegativeXUpNegativeXDown, { |
|
{0.625f, 0.5f}, /* +X */ |
|
{0.125f, 0.5f}, /* -X */ |
|
{0.125f, 0.83333f}, /* +Y */ |
|
{0.125f, 0.16667f}, /* -Y */ |
|
{0.375f, 0.5f}, /* +Z */ |
|
{0.875f, 0.5f} /* -Z */ |
|
}, { |
|
CubeEdge::TopLeft, |
|
CubeEdge::BottomLeft, |
|
CubeEdge::BackLeft, |
|
CubeEdge::BackRight, |
|
CubeEdge::FrontRight, |
|
}}, |
|
/* +-----+ |
|
| +Y | |
|
+-tl--+-----+-----+-----+ |
|
| -X bl +Z br +X fr -Z | |
|
+-----+-bb--+-----+-----+ |
|
| -Y | |
|
+-----+ |
|
0.25 0.5 |
|
0.375 */ |
|
{"-X up, +Z down", CubeFlag::TextureCoordinatesNegativeXUpPositiveZDown, { |
|
{0.625f, 0.5f}, /* +X */ |
|
{0.125f, 0.5f}, /* -X */ |
|
{0.125f, 0.83333f}, /* +Y */ |
|
{0.375f, 0.16667f}, /* -Y */ |
|
{0.375f, 0.5f}, /* +Z */ |
|
{0.875f, 0.5f} /* -Z */ |
|
}, { |
|
CubeEdge::TopLeft, |
|
CubeEdge::BackLeft, |
|
CubeEdge::BottomBack, |
|
CubeEdge::BackRight, |
|
CubeEdge::FrontRight, |
|
}}, |
|
/* +-----+ |
|
| +Y | |
|
+-tl--+-----+-----+-----+ |
|
| -X bl +Z br +X fr -Z | |
|
+-----+-----+-br--+-----+ |
|
| -Y | |
|
+-----+ |
|
0.5 0.75 |
|
0.625 */ |
|
{"-X up, +X down", CubeFlag::TextureCoordinatesNegativeXUpPositiveXDown, { |
|
{0.625f, 0.5f}, /* +X */ |
|
{0.125f, 0.5f}, /* -X */ |
|
{0.125f, 0.83333f}, /* +Y */ |
|
{0.625f, 0.16667f}, /* -Y */ |
|
{0.375f, 0.5f}, /* +Z */ |
|
{0.875f, 0.5f} /* -Z */ |
|
}, { |
|
CubeEdge::TopLeft, |
|
CubeEdge::BackLeft, |
|
CubeEdge::BackRight, |
|
CubeEdge::BottomRight, |
|
CubeEdge::FrontRight, |
|
}}, |
|
/* +-----+ |
|
| +Y | |
|
+-tl--+-----+-----+-----+ |
|
| -X bl +Z br +X fr -Z | |
|
+-----+-----+-----+-bf--+ |
|
| -Y | |
|
+-----+ |
|
0.75 1.0 |
|
0.875 */ |
|
{"-X up, -Z down", CubeFlag::TextureCoordinatesNegativeXUpNegativeZDown, { |
|
{0.625f, 0.5f}, /* +X */ |
|
{0.125f, 0.5f}, /* -X */ |
|
{0.125f, 0.83333f}, /* +Y */ |
|
{0.875f, 0.16667f}, /* -Y */ |
|
{0.375f, 0.5f}, /* +Z */ |
|
{0.875f, 0.5f} /* -Z */ |
|
}, { |
|
CubeEdge::TopLeft, |
|
CubeEdge::BackLeft, |
|
CubeEdge::BackRight, |
|
CubeEdge::FrontRight, |
|
CubeEdge::BottomFront, |
|
}}, |
|
/* +-----+ |
|
| +Y | |
|
+-----+-tb--+-----+-----+ |
|
| -X bl +Z br +X fr -Z | |
|
+-----+-bb--+-----+-----+ |
|
| -Y | |
|
+-----+ |
|
0.25 0.5 |
|
0.375 */ |
|
{"+Z up, +Z down", CubeFlag::TextureCoordinatesPositiveZUpPositiveZDown, { |
|
{0.625f, 0.5f}, /* +X */ |
|
{0.125f, 0.5f}, /* -X */ |
|
{0.375f, 0.83333f}, /* +Y */ |
|
{0.375f, 0.16667f}, /* -Y */ |
|
{0.375f, 0.5f}, /* +Z */ |
|
{0.875f, 0.5f} /* -Z */ |
|
}, { |
|
CubeEdge::TopBack, |
|
CubeEdge::BackLeft, |
|
CubeEdge::BackRight, |
|
CubeEdge::BottomBack, |
|
CubeEdge::FrontRight, |
|
}}, |
|
/* +-----+ |
|
| +Y | |
|
+-----+-tb--+-----+-----+ |
|
| -X bl +Z br +X fr -Z | |
|
+-----+-bb--+-br--+-----+ |
|
| -Y | |
|
+-----+ |
|
0.5 0.75 |
|
0.625 */ |
|
{"+Z up, +X down", CubeFlag::TextureCoordinatesPositiveZUpPositiveXDown, { |
|
{0.625f, 0.5f}, /* +X */ |
|
{0.125f, 0.5f}, /* -X */ |
|
{0.375f, 0.83333f}, /* +Y */ |
|
{0.625f, 0.16667f}, /* -Y */ |
|
{0.375f, 0.5f}, /* +Z */ |
|
{0.875f, 0.5f} /* -Z */ |
|
}, { |
|
CubeEdge::TopBack, |
|
CubeEdge::BackLeft, |
|
CubeEdge::BackRight, |
|
CubeEdge::FrontRight, |
|
CubeEdge::BottomRight, |
|
}}, |
|
}; |
|
|
|
|
|
CubeTest::CubeTest() { |
|
addInstancedTests({&CubeTest::solid}, |
|
Containers::arraySize(SolidData)); |
|
|
|
addInstancedTests<CubeTest>({ |
|
&CubeTest::solidTextureCoordinates, |
|
&CubeTest::solidTextureCoordinates<CubeFlag::Tangents> |
|
}, Containers::arraySize(SolidTextureCoordinatesData)); |
|
|
|
addTests({&CubeTest::solidInvalid, |
|
&CubeTest::solidStrip, |
|
&CubeTest::solidStripGlsl, |
|
&CubeTest::wireframe}); |
|
} |
|
|
|
void CubeTest::solid() { |
|
auto&& data = SolidData[testCaseInstanceId()]; |
|
setTestCaseDescription(data.name); |
|
|
|
Trade::MeshData cube = data.flags ? |
|
Primitives::cubeSolid(*data.flags) : |
|
Primitives::cubeSolid(); |
|
|
|
CORRADE_COMPARE(cube.primitive(), MeshPrimitive::Triangles); |
|
CORRADE_VERIFY(cube.isIndexed()); |
|
CORRADE_COMPARE(cube.indexCount(), 36); |
|
CORRADE_COMPARE(cube.vertexCount(), 24); |
|
CORRADE_COMPARE(cube.attributeCount(), 2); |
|
CORRADE_COMPARE(cube.indices<UnsignedShort>()[17], 11); |
|
CORRADE_COMPARE(cube.attribute<Vector3>(Trade::MeshAttribute::Position)[4], |
|
(Vector3{1.0f, -1.0f, 1.0f})); |
|
CORRADE_COMPARE(cube.attribute<Vector3>(Trade::MeshAttribute::Normal)[6], |
|
(Vector3{1.0f, 0.0f, 0.0f})); |
|
} |
|
|
|
template<class T> T sampleQuad(const T& a, const T& b, const T& c, const T& d, Vector2 t) { |
|
/* Assuming the vertex order is the following, which means the second lerp |
|
has to be in a flipped direction in order to give expected result. |
|
3--2 |
|
| | |
|
0--1 */ |
|
return Math::lerp(Math::lerp(a, b, t[0]), |
|
Math::lerp(d, c, t[0]), t[1]); |
|
} |
|
|
|
template<CubeFlag flag> void CubeTest::solidTextureCoordinates() { |
|
auto&& data = SolidTextureCoordinatesData[testCaseInstanceId()]; |
|
setTestCaseDescription(data.name); |
|
setTestCaseTemplateName(flag == CubeFlag::Tangents ? "CubeFlag::Tangents" : ""); |
|
|
|
Trade::MeshData cube = Primitives::cubeSolid(data.flags|flag); |
|
Containers::StridedArrayView1D<const Vector3> positions = cube.attribute<Vector3>(Trade::MeshAttribute::Position); |
|
Containers::StridedArrayView1D<const Vector2> textureCoordinates = cube.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates); |
|
|
|
/* Same as in solid(), to verify basic sanity */ |
|
CORRADE_COMPARE(cube.primitive(), MeshPrimitive::Triangles); |
|
CORRADE_VERIFY(cube.isIndexed()); |
|
CORRADE_COMPARE(cube.indexCount(), 36); |
|
CORRADE_COMPARE(cube.vertexCount(), 24); |
|
CORRADE_COMPARE(cube.attributeCount(), 3 + (flag == CubeFlag::Tangents ? 1 : 0)); |
|
CORRADE_COMPARE(cube.indices<UnsignedShort>()[17], 11); |
|
CORRADE_COMPARE(positions[4], (Vector3{1.0f, -1.0f, 1.0f})); |
|
CORRADE_COMPARE(cube.attribute<Vector3>(Trade::MeshAttribute::Normal)[6], (Vector3{1.0f, 0.0f, 0.0f})); |
|
|
|
/* Discover which groups of vertices correspond to which faces, in order |
|
matching SolidTextureCoordinatesData::expectedCenters, so +X, -X, +Y, |
|
-Y, +Z, -Z. This could be done just once but who cares, it's just a |
|
test. It could also be hardcoded but that'll make the test tied too much |
|
to the particular data, making it more likely that the test passes with |
|
the data actually being completely wrong. */ |
|
Vector3 faceCenters[6]{ |
|
{+1.0f, 0.0f, 0.0f}, |
|
{-1.0f, 0.0f, 0.0f}, |
|
{0.0f, +1.0f, 0.0f}, |
|
{0.0f, -1.0f, 0.0f}, |
|
{0.0f, 0.0f, +1.0f}, |
|
{0.0f, 0.0f, -1.0f}, |
|
}; |
|
UnsignedInt faceVertexOffsets[6]; |
|
for(UnsignedInt face = 0; face != 6; ++face) { |
|
CORRADE_ITERATION(face); |
|
|
|
Vector3 center = sampleQuad(positions[face*4 + 0], |
|
positions[face*4 + 1], |
|
positions[face*4 + 2], |
|
positions[face*4 + 3], {0.5f, 0.5f}); |
|
UnsignedInt candidate = 0; |
|
for(; candidate != Containers::arraySize(faceCenters); ++candidate) { |
|
if(center == faceCenters[candidate]) { |
|
faceVertexOffsets[candidate] = face*4; |
|
break; |
|
} |
|
} |
|
CORRADE_VERIFY(candidate != Containers::arraySize(faceCenters)); |
|
} |
|
|
|
/* Discover which groups of vertices correspond to which edges, in order |
|
matching the CubeEdge enum above. Same as above, this could be done just |
|
once, or hardcoded, etc., but it's not. */ |
|
Vector3 edgeCenters[12] { |
|
{0.0f, -1.0f, +1.0f}, /* BottomBack */ |
|
{0.0f, -1.0f, -1.0f}, /* BottomFront */ |
|
{0.0f, +1.0f, +1.0f}, /* TopBack */ |
|
{0.0f, +1.0f, -1.0f}, /* TopFront */ |
|
|
|
{-1.0f, 0.0f, +1.0f}, /* BackLeft */ |
|
{+1.0f, 0.0f, +1.0f}, /* BackRight */ |
|
{-1.0f, 0.0f, -1.0f}, /* FrontLeft */ |
|
{+1.0f, 0.0f, -1.0f}, /* FrontRight */ |
|
|
|
{-1.0f, -1.0f, 0.0f}, /* BottomLeft */ |
|
{+1.0f, -1.0f, 0.0f}, /* BottomRight */ |
|
{-1.0f, +1.0f, 0.0f}, /* TopLeft */ |
|
{+1.0f, +1.0f, 0.0f}, /* TopRight */ |
|
}; |
|
/* Each of 12 edges is shared by exactly 2 faces */ |
|
Vector2ui edgeVertices[12][2]; |
|
for(UnsignedInt face = 0; face != 6; ++face) { |
|
CORRADE_ITERATION(face); |
|
|
|
/* Four edges of the quad. Assuming ordering like below, if that |
|
wouldn't be the case, the CORRADE_VERIFY after would fail. |
|
3--2 |
|
| | |
|
0--1 */ |
|
for(Vector2ui edge: { |
|
Vector2ui{face*4 + 0, face*4 + 1}, |
|
Vector2ui{face*4 + 1, face*4 + 2}, |
|
Vector2ui{face*4 + 2, face*4 + 3}, |
|
Vector2ui{face*4 + 3, face*4 + 0} |
|
}) { |
|
Vector3 center = Math::lerp(positions[edge[0]], |
|
positions[edge[1]], 0.5f); |
|
|
|
UnsignedInt candidate = 0; |
|
for(; candidate != Containers::arraySize(edgeCenters); ++candidate) { |
|
if(center == edgeCenters[candidate]) { |
|
if(edgeVertices[candidate][0].isZero()) |
|
edgeVertices[candidate][0] = edge; |
|
else if(edgeVertices[candidate][1].isZero()) |
|
edgeVertices[candidate][1] = edge; |
|
else CORRADE_FAIL("Too many shared edges."); |
|
break; |
|
} |
|
} |
|
CORRADE_ITERATION(edge); |
|
CORRADE_VERIFY(candidate != Containers::arraySize(edgeCenters)); |
|
} |
|
} |
|
/* At this point, if neither the above CORRADE_VERIFY() nor the |
|
CORRADE_FAIL() fire, for each of the 6 faces the 4 edges were assigned, |
|
filling all 24 array entries */ |
|
|
|
/* For each face verify that the sampled texture coordinates at the center |
|
match the expectation */ |
|
for(UnsignedInt face = 0; face != 6; ++face) { |
|
UnsignedInt vertexOffset = faceVertexOffsets[face]; |
|
CORRADE_ITERATION("face" << face << "at offset" << vertexOffset); |
|
Vector2 center = sampleQuad( |
|
textureCoordinates[vertexOffset + 0], |
|
textureCoordinates[vertexOffset + 1], |
|
textureCoordinates[vertexOffset + 2], |
|
textureCoordinates[vertexOffset + 3], {0.5f, 0.5f}); |
|
CORRADE_COMPARE(center, data.expectedCenters[face]); |
|
} |
|
|
|
/* Verify that the expected shared edges indeed have the same texture |
|
coordinates for both faces */ |
|
for(CubeEdge edge: data.expectedSharedEdges) { |
|
/* When we reach an edge that's zero it's the end of the list */ |
|
if(edge == CubeEdge{}) |
|
break; |
|
|
|
/* Sanity check -- the two edges should be filled and have contents |
|
that aren't the same */ |
|
const Vector2ui(&vertices)[2] = edgeVertices[UnsignedInt(edge) - 1]; |
|
CORRADE_ITERATION(edge << "edge with vertices" << Debug::packed << vertices[0] << "and" << Debug::packed << vertices[1]); |
|
CORRADE_VERIFY(!vertices[0].isZero() && |
|
!vertices[1].isZero()); |
|
CORRADE_VERIFY(vertices[0] != vertices[1] && |
|
vertices[0] != vertices[1].flipped()); |
|
|
|
/* The edge should match in one or the other direction */ |
|
Vector2 a0 = textureCoordinates[vertices[0][0]]; |
|
Vector2 a1 = textureCoordinates[vertices[0][1]]; |
|
Vector2 b0 = textureCoordinates[vertices[1][0]]; |
|
Vector2 b1 = textureCoordinates[vertices[1][1]]; |
|
CORRADE_VERIFY((a0 == b0 && a1 == b1) || |
|
(a0 == b1 && a1 == b0)); |
|
} |
|
|
|
/* The texture coordinates should always span the whole [0, 0] to [1, 1] |
|
range. That may mean the faces won't be square if using a square |
|
texture, but in practice the texture would have a size matching the |
|
texture coordinate layout, so e.g. with a 4:3 ratio for the |
|
NegativeXUpNegativeXDown variant. */ |
|
CORRADE_COMPARE(Math::minmax(textureCoordinates), Containers::pair(Vector2{0.0f}, Vector2{1.0f})); |
|
|
|
/* If tangents are enabled, check their properties also */ |
|
if(flag == CubeFlag::Tangents) { |
|
Containers::StridedArrayView1D<const Vector3> normals = cube.attribute<Vector3>(Trade::MeshAttribute::Normal); |
|
Containers::StridedArrayView1D<const Vector4> tangents = cube.attribute<Vector4>(Trade::MeshAttribute::Tangent); |
|
|
|
/* Normals and tangents should be the same for all vertices in a face, |
|
and perpendicular in all cases */ |
|
for(UnsignedInt face = 0; face != 6; ++face) { |
|
CORRADE_ITERATION(face); |
|
CORRADE_COMPARE(Math::dot(normals[face*4], tangents[face*4].xyz()), 0.0f); |
|
CORRADE_COMPARE(normals[face*4].dot(), 1.0f); |
|
CORRADE_COMPARE(tangents[face*4].xyz().dot(), 1.0f); |
|
CORRADE_COMPARE(Math::abs(tangents[face*4].w()), 1.0f); |
|
for(UnsignedInt vertex = 1; vertex != 4; ++vertex) { |
|
CORRADE_ITERATION(vertex); |
|
CORRADE_COMPARE(normals[face*4 + vertex], normals[face*4]); |
|
CORRADE_COMPARE(tangents[face*4 + vertex], tangents[face*4]); |
|
} |
|
} |
|
|
|
/* For each face, sample in a position off center on X and Y */ |
|
for(UnsignedInt face = 0; face != 6; ++face) { |
|
CORRADE_ITERATION(face); |
|
|
|
Vector3 center = sampleQuad( |
|
positions[face*4 + 0], positions[face*4 + 1], |
|
positions[face*4 + 2], positions[face*4 + 3], {0.5f, 0.5f}); |
|
Vector2 centerTexture = sampleQuad( |
|
textureCoordinates[face*4 + 0], textureCoordinates[face*4 + 1], |
|
textureCoordinates[face*4 + 2], textureCoordinates[face*4 + 3], |
|
{0.5f, 0.5f}); |
|
Vector3 tangent = tangents[face*4].xyz(); |
|
Vector3 bitangent = Math::cross(normals[face*4], |
|
tangents[face*4].xyz())*tangents[face*4].w(); |
|
|
|
Vector3 offset[]{ |
|
sampleQuad(positions[face*4 + 0], positions[face*4 + 1], |
|
positions[face*4 + 2], positions[face*4 + 3], |
|
{0.75f, 0.5f}), |
|
sampleQuad(positions[face*4 + 0], positions[face*4 + 1], |
|
positions[face*4 + 2], positions[face*4 + 3], |
|
{0.5f, 0.75f}) |
|
}; |
|
Vector2 offsetTexture[]{ |
|
sampleQuad(textureCoordinates[face*4 + 0], textureCoordinates[face*4 + 1], |
|
textureCoordinates[face*4 + 2], textureCoordinates[face*4 + 3], |
|
{0.75f, 0.5f}), |
|
sampleQuad(textureCoordinates[face*4 + 0], textureCoordinates[face*4 + 1], |
|
textureCoordinates[face*4 + 2], textureCoordinates[face*4 + 3], |
|
{0.5f, 0.75f}) |
|
}; |
|
|
|
for(Int i: {0, 1}) { |
|
CORRADE_ITERATION(i); |
|
|
|
/* If the shift is in direction of tangent, texture coordinates |
|
should be the same in Y and different with a matching sign |
|
in X */ |
|
if(Math::notEqual(Math::dot(offset[i] - center, tangent), 0.0f)) { |
|
Vector2 delta = offsetTexture[i] - centerTexture; |
|
CORRADE_COMPARE(delta.y(), 0.0f); |
|
CORRADE_COMPARE(Math::sign(delta.x()), Math::sign(Math::dot(offset[i] - center, tangent))); |
|
|
|
/* If the shift is in direction of bitangent, texture |
|
coordinates should be the same in X and different with a |
|
matching sign in Y */ |
|
} else if(Math::notEqual(Math::dot(offset[i] - center, bitangent), 0.0f)) { |
|
Vector2 delta = offsetTexture[i] - centerTexture; |
|
CORRADE_COMPARE(delta.x(), 0.0f); |
|
CORRADE_COMPARE(Math::sign(delta.y()), Math::sign(Math::dot(offset[i] - center, bitangent))); |
|
|
|
} else CORRADE_FAIL(Debug::packed << offset[i] - center << "is parallel to neither" << Debug::packed << tangent << "nor" << Debug::packed << bitangent); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void CubeTest::solidInvalid() { |
|
CORRADE_SKIP_IF_NO_ASSERT(); |
|
|
|
Containers::String out; |
|
Error redirectError{&out}; |
|
Primitives::cubeSolid(CubeFlag::Tangents); |
|
Primitives::cubeSolid(CubeFlag(UnsignedInt(CubeFlag::TextureCoordinatesPositiveZUpPositiveXDown) + 2)); |
|
CORRADE_COMPARE_AS(out, |
|
"Primitives::cubeSolid(): a texture coordinate option has to be picked if tangents are enabled\n" |
|
"Primitives::cubeSolid(): unrecognized texture coordinate option 0x12\n", |
|
TestSuite::Compare::String); |
|
} |
|
|
|
void CubeTest::solidStrip() { |
|
Trade::MeshData cube = Primitives::cubeSolidStrip(); |
|
|
|
CORRADE_COMPARE(cube.primitive(), MeshPrimitive::TriangleStrip); |
|
CORRADE_VERIFY(!cube.isIndexed()); |
|
CORRADE_COMPARE(cube.vertexCount(), 14); |
|
CORRADE_COMPARE(cube.attributeCount(), 1); |
|
CORRADE_COMPARE(cube.attribute<Vector3>(Trade::MeshAttribute::Position)[4], |
|
(Vector3{-1.0f, -1.0f, -1.0f})); |
|
} |
|
|
|
void CubeTest::solidStripGlsl() { |
|
Trade::MeshData cube = Primitives::cubeSolidStrip(); |
|
|
|
/* Yes, really. */ |
|
auto solidStripVertex = [](UnsignedInt gl_VertexID) { |
|
typedef Vector3 vec3; |
|
#define mix Math::lerp |
|
#include "data.glsl" |
|
#undef mix |
|
return corner; |
|
}; |
|
|
|
auto vertices = cube.attribute<Vector3>(Trade::MeshAttribute::Position); |
|
for(UnsignedInt i = 0; i != cube.vertexCount(); ++i) { |
|
CORRADE_ITERATION(i); |
|
CORRADE_COMPARE(solidStripVertex(i), vertices[i]); |
|
} |
|
} |
|
|
|
void CubeTest::wireframe() { |
|
Trade::MeshData cube = Primitives::cubeWireframe(); |
|
|
|
CORRADE_COMPARE(cube.primitive(), MeshPrimitive::Lines); |
|
CORRADE_VERIFY(cube.isIndexed()); |
|
CORRADE_COMPARE(cube.indexCount(), 24); |
|
CORRADE_COMPARE(cube.vertexCount(), 8); |
|
CORRADE_COMPARE(cube.attributeCount(), 1); |
|
CORRADE_COMPARE(cube.indices<UnsignedShort>()[5], 3); |
|
CORRADE_COMPARE(cube.attribute<Vector3>(Trade::MeshAttribute::Position)[5], |
|
(Vector3{1.0f, -1.0f, -1.0f})); |
|
} |
|
|
|
}}}} |
|
|
|
CORRADE_TEST_MAIN(Magnum::Primitives::Test::CubeTest)
|
|
|