Sign in to follow this  
Shanjaq

Assets: When to Actually Load?

Recommended Posts

Shanjaq    151
I have an "Asset Manager" class which maintains GUIDs and supplies resource data to the API-specific rendering interface's container classes.

Where is the best place to actually USE this though? I really don't want to be passing an instance of the asset manager around excessively, and making it singleton seems like it would encourage calls from "bad states".

For instance:
calling Entity.SetModel("path/to/model.mdl")

how would the Entity class know anything about the specific instance of Asset Manager?

or just:
setting Entity.Model = "path/to/model.mdl"

and letting the rendering interface notice it wasn't loaded, thus making the call to asset manager?

[Edited by - Shanjaq on October 16, 2010 8:30:38 AM]

Share this post


Link to post
Share on other sites
kloffy    1318
Great question! I've been trying to get my head around this for a good week and I have yet to find a nice solution. Good to hear that I am not the only one having this problem.

My setup looks something like this: I have an Application class which creates the window and the graphics context (in my case OpenGL). This Application class manages a stack of Screens, which represent the high-level states in my game (splash, main menu, in game...). I figured that a nice place to put such an "Asset Manager" would be this Screen class, every Screen would manage its own assets. There could be some duplication between Screens, but let's assume this is not a problem for now. Ok, so in order to render something, I have a rudimentary scene graph consisting out of GroupNode (extends Node) and ObjectNode (extends Node) objects. The ObjectNodes have actual Assets (Mesh, Texture...) associated with them. And this is where I've been going back and forth for a while now: Should the ObjectNodes have a reference to the "Asset Manager"? If so, they would somehow need to be associated with the Screen at some point. However, what about ObjectNodes that haven't been associated with a Screen yet? They wouldn't have reference to a "Asset Manager" and couldn't load assets. Passing the "Asset Manager" around doesn't seem like such a great option either. Perhaps the ObjectNodes shouldn't have a reference to the "Asset Manager" at all?

Anyways, I'm sorry that I couldn't offer any answers. I posted in hopes of making the discussion more concrete and hearing if people have different approaches. I've only mentioned the most basic things and kept them generic, so I expect most of you have such elements in your programs in some way, shape or form. I hope it doesn't derail the thread.

Share this post


Link to post
Share on other sites
Shanjaq    151
No derailing detected. I have a huge tome on "Game Engine Architecture" and it describes all of the functions of a run-time asset manager, but fails to provide any context as to where it could be used. A Sequence Diagram would be most helpful.

Share this post


Link to post
Share on other sites
How about, rather than set the path to model, give the model from the outside?

as in:


Model mdl = assetManager.getModel("path/to/model.mdl");
Entity.setModel(mdl);



this way, Entity class doesn't care about asset manager at all, it just wants Model instance, no matter where it comes from

Share this post


Link to post
Share on other sites
kloffy    1318
Quote:
Original post by i_luv_cplusplus
How about, rather than set the path to model, give the model from the outside?
That is one of the approaches I have considered. I kind of like it. However, it means that it isn't possible to code like this (using the setup I described earlier):

public class EnemyNode : ObjectNode
{
public EnemyNode()
{
Mesh = AssetManager.Load<Mesh>("enemy.obj");
Texture = AssetManager.Load<Texture>("enemy.png");
}
}

public class GameScreen : Screen
{
private long lastSpawnTick;

public override void Update(long Tick)
{
if (Tick - lastSpawnTick > 5000)
{
Root.Add(new EnemyNode());
lastSpawnTick = Tick;
}
}
}

But it would look something like this:

public class EnemyNode : ObjectNode
{
}

public class GameScreen : Screen
{
private long lastSpawnTick;

public EnemyNode CreateEnemy()
{
var enemy = new EnemyNode();
enemy.Mesh = AssetManager.Load<Mesh>("enemy.obj");
enemy.Texture = AssetManager.Load<Texture>("enemy.png");
return enemy;
}

public override void Update(long Tick)
{
if (Tick - lastSpawnTick > 5000)
{
Root.Add(CreateEnemy());
lastSpawnTick = Tick;
}
}
}

Now, the big question is, what are the advantages/disadvantages of either method. Obviously, this is a very simplified example which certainly does not capture all the intricacies of the subject matter...

Share this post


Link to post
Share on other sites
smasherprog    568
OOP (object Oriented Programming) is all about organization. For example, should an EnemyNode like you described above know about the Assetmanager? Well, there is a hierarchy of class-to-class knowledge that must be maintained, and some classes should not know about other classes (this is obvious, isn't it?). To use a crazy example, a std::vector<T> should not have knowledge of the class EnemyNode because the vectors JOB is to just hold "things" regardless of their type --thank you for templates!

So, your EnemyNode SHOULD have knowledge of how to load itself and SHOULD have knowledge of how to do that. So, it SHOULD have knowledge of the AssetManager, otherwise it cannot load itself.

Now, there are instances where this cannot be, it depends on what you are doing. If you are writing a video game for example, then you are more flexible with class-to-class knowledge because the program is designed to run together as a whole, so it wouldn't make sense to have hard separations between some classes. I will get some opposition on this topic because it is all about personal choice.

Writing good code is about a balance: just make sure you do not get out of hand and let all classes know about all other classes, that is a horrendous mistake. The whole point behind writing a class is that it has a JOB TO DO, and you have to give it the tools, and knowledge it needs to complete that JOB. If you want to pass that information into the class through a function call from an outside class, that is fine. If you want to give the class the knowledge to go straight to the AssetManager, that is fine to.

Just do not go crazy like I did in this response :P

This piece of code is wrong
public class EnemyNode : ObjectNode
{
public EnemyNode()
{
Mesh = AssetManager.Load<Mesh>("enemy.obj");
Texture = AssetManager.Load<Texture>("enemy.png");
}
}

Unless you want to have one enemy, this wont work. You should have a manager class for Mesh, Sound, Textures, Physics, etc. Then these Manager classes should take care of the loading, and unloading of their children, playing or setting up of rendering. It should be like a tree. At the top is the Parent, who tells each of the children --MeshManager, SoundManager, PhysicsManager-- to load themselfs, then each loop should be an update called on each of the managers followed by a Run, or you can combine it into a Run call. Each of the managers will then call on their children and update and do their work . . etc

Share this post


Link to post
Share on other sites
Ravyne    14300
Quote:
Original post by smasherprog
OOP (object Oriented Programming) is all about organization. For example, should an EnemyNode like you described above know about the Assetmanager? Well, there is a hierarchy of class-to-class knowledge that must be maintained, and some classes should not know about other classes (this is obvious, isn't it?). To use a crazy example, a std::vector<T> should not have knowledge of the class EnemyNode because the vectors JOB is to just hold "things" regardless of their type --thank you for templates!

So, your EnemyNode SHOULD have knowledge of how to load itself and SHOULD have knowledge of how to do that. So, it SHOULD have knowledge of the AssetManager, otherwise it cannot load itself.


That's a not-entirely-unreasonable approach, but it's not the only one, the "most OOP" one, or probably the best one, either; particularly if you are concerned with having the extensibility to support many model formats.

When we talk about "organization" in an OOP sense we're not talking about putting all the stuff that "feels" like it should go together into one class, we're talking about cohesion and decoupling. These two concepts are basically the crux of the "One class, one responsibility" mantra in object-oriented programming.

The responsibility of an EntityNode (assuming it to be a node in a scene graph) is simply to represent the visual aspect of the entity within the scene. It probably doesn't even hold the actual model data (after all, you don't want to duplicate model data for each instance of an enemy or something) so, why then, would it make sense for it to know how to load something which it does not own?

Then we're left with the model class, whatever that may be. The responsibility of the model class is to represent model data to the underlying graphics API (whether that be OpenGL, Direct3D, or something higher-level, like a scene graph or API abstraction layer.) Even that does not require that the model class know how to load itself, only that it has been loaded at some point.

Loading the model is yet another separate responsibility -- just think about wanting to support another model format, or a new version of an existing format. Why should this require changes to the model class, whose responsibility it is to represent the loaded data to the graphics API? Depending on how well your classes are encapsulated, such changes might cause a massive amount of recompilation to occur, which is something to be avoided.

As for how to structure it and when to use it, I, myself, have either a single, global asset manager (per resource type) whose reference is passed down to each gamestate or I have multiple resource managers (per resource type) owned by the gamestate. In either case resources are loaded at the beginning of each gamestate and none of the resources know anything about the asset manager itself. Loading is accomplished by separate loader classes (function objects) or functions, and specified through a function argument template. That can be cleaned up a bit with the factory pattern if it gets too ugly.

[Edited by - Ravyne on October 16, 2010 11:19:08 PM]

Share this post


Link to post
Share on other sites
_the_phantom_    11250
Just to add to the above, if you are using something like C++ don't feel you need to put EVERYTHING in a class.

Somethings naturally fit better as free functions in namespaces; something the C++ Standard Library shows.

For example, while you might have a 'resouce cache' which knows how to hold onto resources the loading of a resource might well be nothing more than a function in a namespace.

For example;

TexturePtr SomeClass::LoadTexture(const std::string& filename)
{
TexturePtr texture;
if(!cache.hasEntry(filename, texture))
{
texture = TextureLoader::Load(filename);
}
return texture;
}



(Note; the above code should not be considered 'best practise' its just an example)

Share this post


Link to post
Share on other sites
Ravyne    14300
And to add back again to Phantom's input, that is precisely why the loader in my own code (as mentioned) is passed as a template function argument -- this gives compile-time polymorphism allowing the loader to be a function object (a class which overloads 'operator ()' taking some defined parameters to act like a function) or an honest-to-god, free-standing function. Most of the loaders I implement are indeed single functions (for example, .txt, .bmp, .pcx, etc), but its nice to organize more complicated formats that might benefit from helper functions as a class (when data sharing is prominent, otherwise simple, free-standing helper functions can work too) or to pass additional paramters through the class constructor which are not accounted for in the load function's interface.

Share this post


Link to post
Share on other sites
kloffy    1318
Just wanted to say that I appreciate the input and I agree with much of what has been said. However, I'm having difficulties seeing how some of those abstract suggestions would translate into practice. Encapsulation, decoupling, having clear responsibilities assigned to classes etc. are definitely things to strive for. What would be of even greater value is an architecture diagram or a sequence diagram (as shanjaq suggested) in order to get a clearer picture: what classes hold references to the asset manager, when are those references established, at what point are assets loaded/released, how are hierarchical assets dealt with and so on. (Also, the design of the individual loaders seems fairly clear to me, so that should not be the issue here.)

Share this post


Link to post
Share on other sites
Daggerbot    127
I agree that the scene node does not need to know how to load the mesh that it is assigned to draw. It should contain only a pointer to the mesh resource. This doesn't eliminate the possibility to load assets after building a scene graph. If you want all resources to be loaded at once, like during a loading screen for example, leave this up to the resource manager, rather than the scene graph. The way that I have done this is by writing a resource manager that not only keeps track of loaded resources, but also of resources that will later be batch loaded. For example:

// when building the scene
someMesh = meshManager.loadLater("meshes/SomeMesh.obj");
someMeshNode = new MeshNode(someMesh);

// then at some point later
meshManager.loadAll();


In this example, meshManager.loadLater returns a pointer to a mesh object that contains no data, but it also adds the path to the mesh file and the pointer returned to a map. Then meshManager.loadAll iterates this todo map, loading all needed mesh data into respective mesh objects. For convenience, loadAll can be asynchronous:

meshManager.loadAll();
while (meshManager.isLoading())
drawLoadingScreen();

Share this post


Link to post
Share on other sites
newera    157
You still can have the clean creation of entities and decouple the loading of models and entities by introducing another class in which builds the desired entity.


interface EntityFactory {
EntityNode createNode();
}

class EnemyEntityFactory implements EntityFactory {
AssetManager assetMng;
EnemyEntityFactory(AssetManager assetMng) {
this.assertMng = assetMng;
}

EntityNode createNode() {
var enemy = new EnemyNode();
enemy.Mesh = assetMng.Load<Mesh>("enemy.obj");
enemy.Texture = assetMng.Load<Texture>("enemy.png");
return enemy;
}
}

public class GameScreen : Screen
{
private long lastSpawnTick;
private EntityFactory entityFactory;

public override void Update(long Tick)
{
if (Tick - lastSpawnTick > 5000)
{
EntityNode enemyNode = factory.createNode();
Root.Add(enemyNode));
lastSpawnTick = Tick;
}
}
}

Share this post


Link to post
Share on other sites
mixmaster    373
Quote:
Original post by Shanjaq
I have an "Asset Manager" class which maintains GUIDs and supplies resource data to the API-specific rendering interface's container classes.

Where is the best place to actually USE this though? I really don't want to be passing an instance of the asset manager around excessively, and making it singleton seems like it would encourage calls from "bad states".

For instance:
calling Entity.SetModel("path/to/model.mdl")

how would the Entity class know anything about the specific instance of Asset Manager?

or just:
setting Entity.Model = "path/to/model.mdl"

and letting the rendering interface notice it wasn't loaded, thus making the call to asset manager?


Not sure if this helps but in my engine I place the Asset Manager in my base App class, its the first thing I create and last to destroy. when I subclass it to make a game, its just there ready to use.

Only other class in my engine that knows of the Asset Manager is the lsScene node, base of all scenes. I have my scene loader in there which loads all data into the asset manager, then all scene nodes only store a link to the data. Scene nodes can then share data and even be deleted without loosing any REAL data.

Share this post


Link to post
Share on other sites
kloffy    1318
Thank you for your suggestions! I like the Factory approach suggested by newera, it would integrate into my current code very well and it looks nice and flexible. There is one more topic that I have intentionally left out so far, which is serialization. If I wanted to serialize/deserialize my scene graph, using this approach, I would have to let the serializer know about the factories. I'll have to think about a nice way of doing this.

Also thanks to Daggerbot for the comment about asynchronous loading/lazy loading. Looks like an interesting approach, I would definitely like to add something like this.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this