I am writing a game in C++, and something I have noticed is that I have many resource files which need to be loaded after a particular point in initialization.
For example, OpenGL textures and VAOs can't be created until there's a current OpenGL context. Once loaded, they will never be modified, so it is most convenient to make them global.
If these objects could be created without a current OpenGL context, this would be trivial to do:
const texture ground_texture = load_texture("ground.png");
const texture robot_texture = load_texture("robot.png");
const model robot_model = load_model("robot.obj");
where texture
and model
are simple POD structs containing (among other things) OpenGL object IDs.
Instead, I can do this:
texture ground_texture;
texture robot_texture;
model robot_model;
void load_resources() {
ground_texture = load_texture("ground.png");
robot_texture = load_texture("robot.png");
robot_model = load_model("robot.obj");
}
and then call load_resources
after the OpenGL context is initialized. But that requires about twice as much code (three times if these globals are also declared in a header file as well) and loses the const
modifier.
It could conceivably be produced by a simple script to reduce duplication - then the only downside compared to the original non-working code is that the globals aren't const
.
There's also this:
class texture_loader;
class model_loader;
static std::vector<const texture_loader*> tex_loaders;
static std::vector<const model_loader*> model_loaders;
class texture_loader {
mutable texture tex;
mutable bool initialized;
const char *filename;
public:
texture_loader(const char *filename)
: initialized(false), filename(filename)
{
tex_loaders.push_back(this);
}
void load() const {
tex = load_texture(filename);
initialized = true;
}
operator texture() const {
assert(initialized);
return tex;
}
};
// similarly for model_loader
const texture_loader ground_texture("ground.png");
const texture_loader robot_texture("robot.png");
const model_loader robot_model("robot.obj");
void load_textures() {
for(texture_loader *t : tex_loaders) t->load();
for(model_loader *m : model_loaders) m->load();
}
but that seems like quite a lot of complexity for something that is conceptually simple. Also, texture_loader
and model_loader
(and anything_else_loader
) would need to be exposed outside the resource loading module in order for other code to be able to use the globals.
Is this a commonly encountered case? How is it typically solved?