From ca5beb432b34d99cf1148a5a85b4c59a54cab1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 21 Mar 2020 18:01:29 +0100 Subject: [PATCH] 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. --- .../Primitives/Implementation/Spheroid.cpp | 136 ++++++++---------- .../Primitives/Implementation/Spheroid.h | 7 +- 2 files changed, 62 insertions(+), 81 deletions(-) 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)