2
\$\begingroup\$

I'm going through assimp documentation regarding materials. Each material can have multiple textures containing various data (color maps, height maps, etc.) and blending data, etc. I'm trying to understand how to model this efficiently in a data structure, without storing textures for each mesh separately (meaning generating textures and loading the texture (bind and upload to GPU) for each and every mesh in a redundant manner, ignoring that they are re-used).

In this tutorial, they simplify the situation, by just caring about the "1st diffuse color maps". This simplifies the data structure immensely. But, theoretically at least, there can be multiple texture maps for a single material, and textures can be re-used across materials.

I'm trying to understand whether it's common practice to not share any textures between different meshes in a single scene/model.

I have a couple of questions regarding this:

  1. What is the best way to store textures when loading models through assimp? I understand that you can store each texture per mesh per scene (space inefficient but simple). Is there a better way?

  2. I found a few free fbx model that only contains one diffuse color map as a material (similar to the tutorial). Is it common practice for this to happen or did I come across a simplified model?

PS: I understand openGL quite well and I have no problem getting things to render. This question is only concerned about what's practical, efficient and considered best practice.

\$\endgroup\$
  • \$\begingroup\$ Personally, I use a simpleton texture manager. The way mine works is that during a model loading event, I request the texture manager to return an OpenGl texture ID, given a particular texture file name. If it doesn't find any instance of that texture directory in its data banks, then it creates the texture right then and there, and passes back the ID. The same process can work for loading pretty much anything, such as models. \$\endgroup\$ – Yattabyte Feb 21 '16 at 6:37
  • \$\begingroup\$ Hmmm. Singletons seem to be answer to everything. To hell with extending and other OOP crap. :D \$\endgroup\$ – dev_nut Feb 21 '16 at 17:20
  • 2
    \$\begingroup\$ They don't solve everything, but they are useful in this particular situation. \$\endgroup\$ – Yattabyte Feb 21 '16 at 17:50
0
\$\begingroup\$

Generally speaking, rendering engines uses some kind of singleton to manage loaded textures and avoid duplication.

This kind of singleton is often called an asset manager.

Meshes are assigned a material. Materials are composed by example of diffuse color, specular color, diffuse texture, normal maps, glow maps, specular maps, height maps, ...

These material values are often called material parameters. The ones that are related to texture hold a reference to the texture in the asset manager. That means that all meshes that share the same texture only share the same reference to the asset manager, avoiding duplication.

It means that you organised your shaders in a way that a shaders that uses a particular kind of asset uses the exact same name or position for this in all the other shaders.

Before rendering a mesh, you make bindings using the reference held by the asset manager. Some renderers use the same reference in the asset manager than the OpenGL internal reference.

Lastly, using this kind of material management allows you to actively sort meshes before rendering based on their material parameters in order to reduce OpenGL state changes that are costly.

Of course, material is only one of the sorting criteria, along with transparency, z order, ...

Hope it helps!

\$\endgroup\$
  • \$\begingroup\$ Hmmm. I know about a singleton Asset Managers. I guess my question was specifically about how to re-use Textures and Material relationships present in the Assimp representation of FBX. \$\endgroup\$ – dev_nut Dec 20 '15 at 17:58
0
\$\begingroup\$

I have run into this problem for my own game engine currently being designed. The method I used was to store the all the loaded textures into a hash table with the path as its key: std::unordered_map<std::string, Texture>. This data structure will be managed by a ResourceManager or whatever you want to call. Every time you want to retrieve a texture you query the ResourceManager with the key. If it doesn't exist, the ResourceManager is responsible for instantiating the texture and return a pointer. Basically something looks like this

Texture *ResourceManager::getTexture(const std::string &path, const TextureType& type)
{
    auto t = m_textures.find(path);
    if (t == m_textures.end())
    {
        return loadTexture(path, type);
    }
    return &t->second;
}

And the relationship between Texture, Material and Mesh (and shaders) can be as follow:

Meshes contain pointers to the material, and materials will have the shader program object and pointers to the textures whom the fragment shader will use. And all three kinds of objects can be stored using the method we discussed above. So when you import from Assimp you will load all the resources separately and only reference each other. The above structure will be useful when you want to dynamically use material and meshes. for example, you can give your brick material to the human mesh or the wall mesh.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.