Browse Source

Primitives: make internal spheroid generator less rigid.

To allow introducing tangents and possibly other attributes. This means
the attribute data isn't defined at compile time anymore, but it
would make further additions too annoying to do due to combinatorial
explosion of all variants.
pull/430/head
Vladimír Vondruš 6 years ago
parent
commit
ca5beb432b
  1. 136
      src/Magnum/Primitives/Implementation/Spheroid.cpp
  2. 7
      src/Magnum/Primitives/Implementation/Spheroid.h

136
src/Magnum/Primitives/Implementation/Spheroid.cpp

@ -35,48 +35,41 @@
namespace Magnum { namespace Primitives { namespace Implementation { namespace Magnum { namespace Primitives { namespace Implementation {
Spheroid::Spheroid(UnsignedInt segments, Flags flags): _segments(segments), _flags{flags} {} Spheroid::Spheroid(UnsignedInt segments, Flags flags): _segments(segments), _flags{flags}, _stride{sizeof(Vector3) + sizeof(Vector3)}, _attributeCount{2} {
if(_flags & Flag::TextureCoordinates) {
namespace { _textureCoordinateOffset = _stride;
_stride += sizeof(Vector2);
struct Vertex { ++_attributeCount;
Vector3 position; } else _textureCoordinateOffset = ~std::size_t{};
Vector3 normal;
};
struct VertexTextureCoords {
Vector3 position;
Vector3 normal;
Vector2 textureCoords;
};
} }
/** @todo gah this is fugly, any idea how to do this less awful? expose
arrayGrow? also, with current growth strategy this might realloc too much
at the beginning since the growth is optimized for adding a single
element */
void Spheroid::append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords) { void Spheroid::append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords) {
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(&position, 1)));
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(&normal, 1)));
if(_flags & Flag::TextureCoordinates) { if(_flags & Flag::TextureCoordinates) {
const VertexTextureCoords v[]{{position, normal, textureCoords}};
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData, Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(v))); Containers::arrayCast<const char>(Containers::arrayView(&textureCoords, 1)));
} else {
const Vertex v[]{{position, normal}};
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(v)));
} }
} }
void Spheroid::setLastVertexTextureCoords(const Vector2& textureCoords) { Vector3 Spheroid::lastVertexPosition(const std::size_t offsetFromEnd) {
/* Assuming append() was called before */ return Containers::arrayCast<Vector3>(_vertexData.slice<sizeof(Vector3)>(_vertexData.size() - _stride*offsetFromEnd))[0];
Containers::arrayCast<VertexTextureCoords>(_vertexData).back().textureCoords = textureCoords; }
Vector3 Spheroid::lastVertexNormal(const std::size_t offsetFromEnd) {
return Containers::arrayCast<Vector3>(_vertexData.slice<sizeof(Vector3)>(_vertexData.size() - _stride*offsetFromEnd + sizeof(Vector3)))[0];
}
Vector2& Spheroid::lastVertexTextureCoords(const std::size_t offsetFromEnd) {
return Containers::arrayCast<Vector2>(_vertexData.slice<sizeof(Vector2)>(_vertexData.size() - _stride*offsetFromEnd + _textureCoordinateOffset))[0];
} }
void Spheroid::capVertex(Float y, Float normalY, Float textureCoordsV) { void Spheroid::capVertex(Float y, Float normalY, Float textureCoordsV) {
append({0.0f, y, 0.0f}, {0.0f, normalY, 0.0f}); append({0.0f, y, 0.0f}, {0.0f, normalY, 0.0f});
if(_flags & Flag::TextureCoordinates) if(_flags & Flag::TextureCoordinates)
setLastVertexTextureCoords({0.5f, textureCoordsV}); lastVertexTextureCoords(1) = {0.5f, textureCoordsV};
} }
void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad startRingAngle, Rad ringAngleIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement) { void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad startRingAngle, Rad ringAngleIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement) {
@ -95,16 +88,14 @@ void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad start
{x*segmentSinCos.first, y, z*segmentSinCos.second}); {x*segmentSinCos.first, y, z*segmentSinCos.second});
if(_flags & Flag::TextureCoordinates) if(_flags & Flag::TextureCoordinates)
setLastVertexTextureCoords({j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement}); lastVertexTextureCoords(1) = {j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement};
} }
/* Duplicate first segment in the ring for additional vertex for texture coordinate */ /* Duplicate first segment in the ring for additional vertex for
texture coordinate */
if(_flags & Flag::TextureCoordinates) { if(_flags & Flag::TextureCoordinates) {
/* This view will become dangling right after append() */ append(lastVertexPosition(_segments), lastVertexNormal(_segments),
auto typedVertices = Containers::arrayCast<VertexTextureCoords>(_vertexData); {1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
append(typedVertices[typedVertices.size()-_segments].position,
typedVertices[typedVertices.size()-_segments].normal,
{1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
} }
} }
} }
@ -122,16 +113,13 @@ void Spheroid::cylinderVertexRings(const UnsignedInt count, const Float startY,
{baseNormal.x()*segmentSinCos.first, baseNormal.y(), baseNormal.x()*segmentSinCos.second}); {baseNormal.x()*segmentSinCos.first, baseNormal.y(), baseNormal.x()*segmentSinCos.second});
if(_flags & Flag::TextureCoordinates) if(_flags & Flag::TextureCoordinates)
setLastVertexTextureCoords({j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement}); lastVertexTextureCoords(1) = {j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement};
} }
/* Duplicate first segment in the ring for additional vertex for texture coordinate */ /* Duplicate first segment in the ring for additional vertex for texture coordinate */
if(_flags & Flag::TextureCoordinates) { if(_flags & Flag::TextureCoordinates) {
/* This view will become dangling right after append() */ append(lastVertexPosition(_segments), lastVertexNormal(_segments),
auto typedVertices = Containers::arrayCast<VertexTextureCoords>(_vertexData); {1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
append(typedVertices[typedVertices.size()-_segments].position,
typedVertices[typedVertices.size()-_segments].normal,
{1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
} }
base += increment; base += increment;
@ -180,11 +168,7 @@ void Spheroid::faceRings(UnsignedInt count, UnsignedInt offset) {
void Spheroid::topFaceRing() { void Spheroid::topFaceRing() {
const UnsignedInt vertexSegments = _segments + (_flags & Flag::TextureCoordinates ? 1 : 0); const UnsignedInt vertexSegments = _segments + (_flags & Flag::TextureCoordinates ? 1 : 0);
UnsignedInt vertexCount; const UnsignedInt vertexCount = _vertexData.size()/_stride;
if(_flags & Flag::TextureCoordinates)
vertexCount = _vertexData.size()/sizeof(VertexTextureCoords);
else
vertexCount = _vertexData.size()/sizeof(Vertex);
for(UnsignedInt j = 0; j != _segments; ++j) { for(UnsignedInt j = 0; j != _segments; ++j) {
Containers::arrayAppend<Trade::ArrayAllocator>(_indexData, { Containers::arrayAppend<Trade::ArrayAllocator>(_indexData, {
@ -210,52 +194,44 @@ void Spheroid::capVertexRing(Float y, Float textureCoordsV, const Vector3& norma
append({segmentSinCos.first, y, segmentSinCos.second}, normal); append({segmentSinCos.first, y, segmentSinCos.second}, normal);
if(_flags & Flag::TextureCoordinates) if(_flags & Flag::TextureCoordinates)
setLastVertexTextureCoords({i*1.0f/_segments, textureCoordsV}); lastVertexTextureCoords(1) = {i*1.0f/_segments, textureCoordsV};
} }
/* Duplicate first segment in the ring for additional vertex for texture coordinate */ /* Duplicate first segment in the ring for additional vertex for texture
coordinate */
if(_flags & Flag::TextureCoordinates) { if(_flags & Flag::TextureCoordinates) {
/* This view will become dangling right after append() */ append(lastVertexPosition(_segments), normal, {1.0f, textureCoordsV});
auto typedVertices = Containers::arrayCast<VertexTextureCoords>(_vertexData);
append(typedVertices[typedVertices.size()-_segments].position,
normal,
{1.0f, textureCoordsV});
} }
} }
namespace {
constexpr Trade::MeshAttributeData AttributeData[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3,
offsetof(Vertex, position), 0, sizeof(Vertex)},
Trade::MeshAttributeData{Trade::MeshAttribute::Normal, VertexFormat::Vector3,
offsetof(Vertex, normal), 0, sizeof(Vertex)}
};
constexpr Trade::MeshAttributeData AttributeDataTextureCoords[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3,
offsetof(VertexTextureCoords, position), 0, sizeof(VertexTextureCoords)},
Trade::MeshAttributeData{Trade::MeshAttribute::Normal, VertexFormat::Vector3,
offsetof(VertexTextureCoords, normal), 0, sizeof(VertexTextureCoords)},
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, VertexFormat::Vector2,
offsetof(VertexTextureCoords, textureCoords), 0, sizeof(VertexTextureCoords)}
};
}
Trade::MeshData Spheroid::finalize() { Trade::MeshData Spheroid::finalize() {
Trade::MeshIndexData indices{_indexData}; Trade::MeshIndexData indices{_indexData};
Containers::Array<Trade::MeshAttributeData> attributes; std::size_t attributeOffset = 0;
Containers::Array<Trade::MeshAttributeData> attributes{_attributeCount};
attributes[attributeOffset++] = Trade::MeshAttributeData{
Trade::MeshAttribute::Position, VertexFormat::Vector3,
Containers::stridedArrayView(_vertexData,
_vertexData.data(),
_vertexData.size()/_stride, std::ptrdiff_t(_stride))};
attributes[attributeOffset++] = Trade::MeshAttributeData{
Trade::MeshAttribute::Normal, VertexFormat::Vector3,
Containers::stridedArrayView(_vertexData,
_vertexData.data() + sizeof(Vector3),
_vertexData.size()/_stride, std::ptrdiff_t(_stride))};
if(_flags & Flag::TextureCoordinates) if(_flags & Flag::TextureCoordinates)
attributes = Trade::meshAttributeDataNonOwningArray(AttributeDataTextureCoords); attributes[attributeOffset++] = Trade::MeshAttributeData{
else Trade::MeshAttribute::TextureCoordinates, VertexFormat::Vector2,
attributes = Trade::meshAttributeDataNonOwningArray(AttributeData); Containers::stridedArrayView(_vertexData,
const UnsignedInt vertexCount = _vertexData.size()/attributes[0].stride(); _vertexData.data() + _textureCoordinateOffset,
_vertexData.size()/_stride, std::ptrdiff_t(_stride))};
CORRADE_INTERNAL_ASSERT(attributeOffset == _attributeCount);
return Trade::MeshData{MeshPrimitive::Triangles, return Trade::MeshData{MeshPrimitive::Triangles,
Containers::arrayAllocatorCast<char, Trade::ArrayAllocator>(std::move(_indexData)), Containers::arrayAllocatorCast<char, Trade::ArrayAllocator>(std::move(_indexData)),
indices, std::move(_vertexData), std::move(attributes), vertexCount}; indices, std::move(_vertexData), std::move(attributes)};
} }
}}} }}}

7
src/Magnum/Primitives/Implementation/Spheroid.h

@ -57,12 +57,17 @@ class Spheroid {
private: private:
UnsignedInt _segments; UnsignedInt _segments;
Flags _flags; Flags _flags;
std::size_t _stride;
std::size_t _textureCoordinateOffset;
std::size_t _attributeCount;
Containers::Array<UnsignedInt> _indexData; Containers::Array<UnsignedInt> _indexData;
Containers::Array<char> _vertexData; Containers::Array<char> _vertexData;
void append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords = {}); void append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords = {});
void setLastVertexTextureCoords(const Vector2& textureCoords); Vector3 lastVertexPosition(std::size_t offsetFromEnd);
Vector3 lastVertexNormal(std::size_t offsetFromEnd);
Vector2& lastVertexTextureCoords(std::size_t offsetFromEnd);
}; };
CORRADE_ENUMSET_OPERATORS(Spheroid::Flags) CORRADE_ENUMSET_OPERATORS(Spheroid::Flags)

Loading…
Cancel
Save