Skip to content

Commit

Permalink
Primitives: make internal spheroid generator less rigid.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mosra committed Mar 22, 2020
1 parent ff3e771 commit ca5beb4
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 81 deletions.
136 changes: 56 additions & 80 deletions src/Magnum/Primitives/Implementation/Spheroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<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) {
const VertexTextureCoords v[]{{position, normal, textureCoords}};
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(v)));
} else {
const Vertex v[]{{position, normal}};
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(v)));
Containers::arrayCast<const char>(Containers::arrayView(&textureCoords, 1)));
}
}

void Spheroid::setLastVertexTextureCoords(const Vector2& textureCoords) {
/* Assuming append() was called before */
Containers::arrayCast<VertexTextureCoords>(_vertexData).back().textureCoords = textureCoords;
Vector3 Spheroid::lastVertexPosition(const std::size_t offsetFromEnd) {
return Containers::arrayCast<Vector3>(_vertexData.slice<sizeof(Vector3)>(_vertexData.size() - _stride*offsetFromEnd))[0];
}

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) {
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) {
Expand All @@ -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<VertexTextureCoords>(_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});
}
}
}
Expand All @@ -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<VertexTextureCoords>(_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;
Expand Down Expand Up @@ -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<Trade::ArrayAllocator>(_indexData, {
Expand All @@ -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<VertexTextureCoords>(_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<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)
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<char, Trade::ArrayAllocator>(std::move(_indexData)),
indices, std::move(_vertexData), std::move(attributes), vertexCount};
indices, std::move(_vertexData), std::move(attributes)};
}

}}}
7 changes: 6 additions & 1 deletion src/Magnum/Primitives/Implementation/Spheroid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<UnsignedInt> _indexData;
Containers::Array<char> _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)
Expand Down

0 comments on commit ca5beb4

Please sign in to comment.