I am working on a C++/OpenGL 3D mesh modeling software. Basically an extremely primitive Blender.
A Mesh
contains a cache of vertices (EditCache<Vertex>
class below), whose vertex data is stored in a single chunk to send to glBufferData. That is the vertices
vector below. Then associated with each vertex, is a dynamic array of edge indices and face indices which refer to the IDs of edges and faces which contain that vertex.
template<typename T>
class EditCache {
private:
std::vector<T> vertices;
std::vector<std::vector<int>> edge_indices;
std::vector<std::vector<int>> face_indices;
public:
int addVertex(T vertex) {
vertices.push_back(vertex);
edge_indices.push_back(std::vector<int>());
face_indices.push_back(std::vector<int>());
return vertices.size() - 1;
}
void pairEdgeIndex(int vertex_index, int edge_index) {
edge_indices[point_index].push_back(edge_index);
}
void pairFaceIndex(int vertex_index, int face_index) {
face_indices[point_index].push_back(face_index);
}
...
};
When a new edge or face is created in the Mesh
class out of existing vertices, the ID of the new edge/face is pushed back to the array in edge_indices
/face_indices
associated with each vertex that is in the edge/face. When a new vertex is created, it is pushed back onto the vertices
array, as well as pushing back two new empty vectors in at the new vertex's spot to hold the list of edges and faces that the new vertex is contained in.
Roughly the data structure looks like this (not how it is stored in memory).
- vertex_0 | { edge_0, ..., edge_n } | { face_0, ..., face_m }
- vertex_1 | { edge_0, ..., edge_n } | { face_0, ..., face_m }
- vertex_2 | { edge_0, ..., edge_n } | { face_0, ..., face_m }
- ...
where each edge or face list is variable length. In memory, each column (vertex, edge-index, and face-index) are three separate arrays.
When creating new quads, faces or bigger structures with many new vertices, I will have to perform multiple std::vector.push_back()
many times to create new vertices and to pair each vertex to each of its new edges and faces. And allocating memory one by one is really slow. The catches where I think I have room to optimize are:
1.) The arrays for the edge and face IDs do not have to be continuous, they can be linked lists.
2.) I will know how many points I will be adding at the beginning of the task.
So in a perfect world, I would allocate a single chunk of the size I need and then append each of the data points to each linked list one by one in that new space. How can I accomplish this task? Naturally, I don't like having exposed new
and delete
operations as well as having exposed raw pointers to free space. Is there a way I can do this with std::unique_ptr
or std::shared_ptr
? How can I allocate the memory in a safe way?