diff --git a/src/Magnum/Primitives/Implementation/Spheroid.cpp b/src/Magnum/Primitives/Implementation/Spheroid.cpp index 874130b04..09c030aea 100644 --- a/src/Magnum/Primitives/Implementation/Spheroid.cpp +++ b/src/Magnum/Primitives/Implementation/Spheroid.cpp @@ -35,48 +35,41 @@ namespace Magnum { namespace Primitives { namespace Implementation { -Spheroid::Spheroid(UnsignedInt segments, Flags flags): _segments(segments), _flags{flags} {} - -namespace { - -struct Vertex { - Vector3 position; - Vector3 normal; -}; - -struct VertexTextureCoords { - Vector3 position; - Vector3 normal; - Vector2 textureCoords; -}; - +Spheroid::Spheroid(UnsignedInt segments, Flags flags): _segments(segments), _flags{flags}, _stride{sizeof(Vector3) + sizeof(Vector3)}, _attributeCount{2} { + if(_flags & Flag::TextureCoordinates) { + _textureCoordinateOffset = _stride; + _stride += sizeof(Vector2); + ++_attributeCount; + } else _textureCoordinateOffset = ~std::size_t{}; } -/** @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) { + Containers::arrayAppend(_vertexData, + Containers::arrayCast(Containers::arrayView(&position, 1))); + Containers::arrayAppend(_vertexData, + Containers::arrayCast(Containers::arrayView(&normal, 1))); if(_flags & Flag::TextureCoordinates) { - const VertexTextureCoords v[]{{position, normal, textureCoords}}; Containers::arrayAppend(_vertexData, - Containers::arrayCast(Containers::arrayView(v))); - } else { - const Vertex v[]{{position, normal}}; - Containers::arrayAppend(_vertexData, - Containers::arrayCast(Containers::arrayView(v))); + Containers::arrayCast(Containers::arrayView(&textureCoords, 1))); } } -void Spheroid::setLastVertexTextureCoords(const Vector2& textureCoords) { - /* Assuming append() was called before */ - Containers::arrayCast(_vertexData).back().textureCoords = textureCoords; +Vector3 Spheroid::lastVertexPosition(const std::size_t offsetFromEnd) { + return Containers::arrayCast(_vertexData.slice(_vertexData.size() - _stride*offsetFromEnd))[0]; +} + +Vector3 Spheroid::lastVertexNormal(const std::size_t offsetFromEnd) { + return Containers::arrayCast(_vertexData.slice(_vertexData.size() - _stride*offsetFromEnd + sizeof(Vector3)))[0]; +} + +Vector2& Spheroid::lastVertexTextureCoords(const std::size_t offsetFromEnd) { + return Containers::arrayCast(_vertexData.slice(_vertexData.size() - _stride*offsetFromEnd + _textureCoordinateOffset))[0]; } void Spheroid::capVertex(Float y, Float normalY, Float textureCoordsV) { append({0.0f, y, 0.0f}, {0.0f, normalY, 0.0f}); 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) { @@ -95,16 +88,14 @@ void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad start {x*segmentSinCos.first, y, z*segmentSinCos.second}); 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) { - /* This view will become dangling right after append() */ - auto typedVertices = Containers::arrayCast(_vertexData); - append(typedVertices[typedVertices.size()-_segments].position, - typedVertices[typedVertices.size()-_segments].normal, - {1.0f, startTextureCoordsV + i*textureCoordsVIncrement}); + append(lastVertexPosition(_segments), lastVertexNormal(_segments), + {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}); 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 */ if(_flags & Flag::TextureCoordinates) { - /* This view will become dangling right after append() */ - auto typedVertices = Containers::arrayCast(_vertexData); - append(typedVertices[typedVertices.size()-_segments].position, - typedVertices[typedVertices.size()-_segments].normal, - {1.0f, startTextureCoordsV + i*textureCoordsVIncrement}); + append(lastVertexPosition(_segments), lastVertexNormal(_segments), + {1.0f, startTextureCoordsV + i*textureCoordsVIncrement}); } base += increment; @@ -180,11 +168,7 @@ void Spheroid::faceRings(UnsignedInt count, UnsignedInt offset) { void Spheroid::topFaceRing() { const UnsignedInt vertexSegments = _segments + (_flags & Flag::TextureCoordinates ? 1 : 0); - UnsignedInt vertexCount; - if(_flags & Flag::TextureCoordinates) - vertexCount = _vertexData.size()/sizeof(VertexTextureCoords); - else - vertexCount = _vertexData.size()/sizeof(Vertex); + const UnsignedInt vertexCount = _vertexData.size()/_stride; for(UnsignedInt j = 0; j != _segments; ++j) { Containers::arrayAppend(_indexData, { @@ -210,52 +194,44 @@ void Spheroid::capVertexRing(Float y, Float textureCoordsV, const Vector3& norma append({segmentSinCos.first, y, segmentSinCos.second}, normal); 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) { - /* This view will become dangling right after append() */ - auto typedVertices = Containers::arrayCast(_vertexData); - append(typedVertices[typedVertices.size()-_segments].position, - normal, - {1.0f, textureCoordsV}); + append(lastVertexPosition(_segments), 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::MeshIndexData indices{_indexData}; - Containers::Array attributes; + std::size_t attributeOffset = 0; + Containers::Array 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) - attributes = Trade::meshAttributeDataNonOwningArray(AttributeDataTextureCoords); - else - attributes = Trade::meshAttributeDataNonOwningArray(AttributeData); - const UnsignedInt vertexCount = _vertexData.size()/attributes[0].stride(); + attributes[attributeOffset++] = Trade::MeshAttributeData{ + Trade::MeshAttribute::TextureCoordinates, VertexFormat::Vector2, + Containers::stridedArrayView(_vertexData, + _vertexData.data() + _textureCoordinateOffset, + _vertexData.size()/_stride, std::ptrdiff_t(_stride))}; + + CORRADE_INTERNAL_ASSERT(attributeOffset == _attributeCount); return Trade::MeshData{MeshPrimitive::Triangles, Containers::arrayAllocatorCast(std::move(_indexData)), - indices, std::move(_vertexData), std::move(attributes), vertexCount}; + indices, std::move(_vertexData), std::move(attributes)}; } }}} diff --git a/src/Magnum/Primitives/Implementation/Spheroid.h b/src/Magnum/Primitives/Implementation/Spheroid.h index def6262df..79253bf77 100644 --- a/src/Magnum/Primitives/Implementation/Spheroid.h +++ b/src/Magnum/Primitives/Implementation/Spheroid.h @@ -57,12 +57,17 @@ class Spheroid { private: UnsignedInt _segments; Flags _flags; + std::size_t _stride; + std::size_t _textureCoordinateOffset; + std::size_t _attributeCount; Containers::Array _indexData; Containers::Array _vertexData; 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)