The function's job is to load a vertex attribute buffer or an element buffer from a specified path, and upload it to OpenGL. Also, optionally, return the number of vertices/indices loaded through an output parameter.
Signature:
GLuint loadBufferFromFile(std::string const& bufferPath, unsigned char vertexElementCount, GLenum vertexFormat, GLenum bufferType=GL_ARRAY_BUFFER, GLuint *vertexCount=NULL, GLenum usageHint=GL_STATIC_DRAW);
Body:
GLuint loadBufferFromFile(std::string const& bufferPath, unsigned char vertexElementCount, GLenum vertexFormat, GLenum bufferType, GLuint *vertexCount, GLenum usageHint)
{
const std::unordered_set<GLenum> VALID_VERTEX_FORMATS = {GL_FLOAT, GL_UNSIGNED_INT, GL_UNSIGNED_BYTE};
//bufferType and usageHint will be validated by OpenGL
if(vertexElementCount < 1 || vertexElementCount > 4)
{
throw std::invalid_argument("Vertex element count must be in range <1,4>");
}
if(bufferType == GL_ELEMENT_ARRAY_BUFFER && vertexElementCount != 1)
{
throw std::invalid_argument("Buffer of type GL_ELEMENT_ARRAY_BUFFER must have element count of 1");
}
if(VALID_VERTEX_FORMATS.find(vertexFormat) == VALID_VERTEX_FORMATS.end())
{
throw std::invalid_argument("Invalid vertex format");
}
std::ifstream file;
file.open(bufferPath, std::ios::binary | std::ios::in);
if(!file.is_open())
{
throw std::runtime_error(std::string("Buffer file ") + bufferPath + " could not be opened");
}
file.seekg(0, file.end);
unsigned int length = file.tellg();
file.seekg(0, file.beg);
if(vertexCount != NULL)
{
std::size_t vertexSize;
switch(vertexFormat)
{
case GL_FLOAT:
vertexSize = sizeof(GLfloat);
break;
case GL_UNSIGNED_INT:
vertexSize = sizeof(GLuint);
break;
case GL_UNSIGNED_BYTE:
vertexSize = sizeof(GLubyte);
break;
}
vertexSize *= vertexElementCount;
*vertexCount = length/vertexSize;
}
char *buffer = new char [length];
file.read(buffer, length);
file.close();
GLuint bufferID;
glGenBuffers(1, &bufferID);
glBindBuffer(bufferType, bufferID);
glBufferData(bufferType, length, buffer, usageHint);
delete[] buffer;
return bufferID;
}
Specific questions:
- Is it appropriate to fine-tune data types when it is not strictly necessary, like I did with
vertexElementCount
?int
would have also worked just fine. - Is this level of error handling/validation appropriate?
- Is it good to use a
const
collection for validation in this way? Are there runtime costs beyond the act of validation itself? - If yes,
unordered_set
orvector
? On one hand, a set seems a logical choice for this, on the other, wisdom of the ancients says "Use vector for everything unless you absolutely can't!" - Are those appropriate exceptions to use?
- Even though in this case I can be certain it will not lead to a memory leak (nothing in between
new
anddelete
throws exceptions), directly using dynamic allocation rubs me the wrong way. Can I avoid it in a reasonable way?