feat: subdivide isosphere

https://schneide.blog/2016/07/15/generating-an-icosphere-in-c/
This commit is contained in:
Cat Flynn 2023-08-03 01:02:51 +02:00
parent 74ccc647f4
commit a444deb51a
2 changed files with 81 additions and 8 deletions

View File

@ -1,32 +1,46 @@
#include "icosphere.hpp"
#include <iostream>
#include <array>
Icosphere::Icosphere(int subdivisions)
{
VertexList vertices = _isocahedronVertices;
TriangleList triangles = _isocahedronTriangles;
for (int i = 0; i < subdivisions; i++)
{
triangles = subdivide(vertices, triangles);
}
std::cout <<
"subdivisions: " << subdivisions <<
" vertices: " << vertices.size() <<
" triangles: " << triangles.size() << std::endl;
glGenVertexArrays(1, &_vao);
glGenBuffers(1, &_vbo);
glGenBuffers(1, &_ebo);
glBindVertexArray(_vao);
for (const auto& v : _isocahedronVertices)
for (const auto& v : vertices)
{
_vertices.push_back(v[0]);
_vertices.push_back(v[1]);
_vertices.push_back(v[2]);
}
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(float), &_vertices[0], GL_STATIC_DRAW);
size_t vboBufferSize = _vertices.size() * sizeof(float);
glBufferData(GL_ARRAY_BUFFER, vboBufferSize, &_vertices[0], GL_STATIC_DRAW);
for (const auto& tri : _isocahedronTriangles)
for (const auto& tri : triangles)
{
_indices.push_back(tri.vertex[0]);
_indices.push_back(tri.vertex[1]);
_indices.push_back(tri.vertex[2]);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(unsigned int), &_indices[0], GL_STATIC_DRAW);
size_t egoBufferSize = _indices.size() * sizeof(unsigned int);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, egoBufferSize, &_indices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
@ -36,6 +50,59 @@ Icosphere::Icosphere(int subdivisions)
glBindVertexArray(0);
}
int Icosphere::vertexForEdge(Lookup& lookup, VertexList& vertices, int first, int second)
{
Lookup::key_type key(first, second);
// Normalize edge directions so that we don't store edges twice
if (key.first > key.second)
{
std::swap(key.first, key.second);
}
// .insert returns a pair with a pointer to the newly-inserted (or already
// existing) element in the lookup as the first element and a boolean
// indicating a successful new insertion as the second.
auto inserted = lookup.insert({key, vertices.size()});
// If the insertion was successful - i.e this is a newly inserted element
if (inserted.second)
{
// Two vectors determined by two existing points
auto& edge0 = vertices[first];
auto& edge1 = vertices[second];
// Combine and normalize the vectors to get the halfway point on the
// unit sphere.
auto point = normalize(edge0 + edge1);
vertices.push_back(point);
}
return inserted.first->second;
}
Icosphere::TriangleList Icosphere::subdivide(VertexList& vertices, TriangleList triangles)
{
Lookup lookup;
TriangleList result;
for (auto&& triangle : triangles)
{
std::array<int, 3> mid;
for (int edge = 0; edge < 3; edge++)
{
mid[edge] = vertexForEdge(lookup, vertices,
triangle.vertex[edge], triangle.vertex[(edge + 1) % 3]);
}
// Create four new triangles from the original triangle
result.push_back({triangle.vertex[0], mid[0], mid[2]});
result.push_back({triangle.vertex[1], mid[1], mid[0]});
result.push_back({triangle.vertex[2], mid[2], mid[1]});
result.push_back({mid[0], mid[1], mid[2]});
}
return result;
}
void Icosphere::render()
{
glBindVertexArray(_vao);

View File

@ -3,6 +3,7 @@
#include <GL/glew.h>
#include <vector>
#include <string>
#include <map>
#include "glm/glm.hpp"
@ -22,12 +23,12 @@ private:
std::vector<float> _vertices;
std::vector<unsigned int> _indices;
struct IcoTriangle
struct Triangle
{
unsigned int vertex[3];
int vertex[3];
};
using IcoTriangleList = std::vector<IcoTriangle>;
using TriangleList = std::vector<Triangle>;
using VertexList = std::vector<glm::vec3>;
const float X=.525731112119133606f;
@ -41,11 +42,16 @@ private:
{Z,X,N}, {-Z,X, N}, {Z,-X,N}, {-Z,-X, N}
};
const IcoTriangleList _isocahedronTriangles =
const TriangleList _isocahedronTriangles =
{
{0,4,1},{0,9,4},{9,5,4},{4,5,8},{4,8,1},
{8,10,1},{8,3,10},{5,3,8},{5,2,3},{2,7,3},
{7,10,3},{7,6,10},{7,11,6},{11,0,6},{0,1,6},
{6,1,10},{9,0,11},{9,11,2},{9,2,5},{7,2,11}
};
using Lookup = std::map<std::pair<int, int>, int>;
int vertexForEdge(Lookup& lookup, VertexList& vertices, int first, int second);
TriangleList subdivide(VertexList& vertices, TriangleList triangles);
};