Page 1 of 4

Distant objects

Posted: 06 Sep 2017, 18:31
by scrawl
As with shadows, I won't have any time to work on this, but I can give my 2 cents on how it should be done.

I actually wrote all this a while ago, to someone who asked, but said person stopped replying back so I think it's safe to say the task is up for grabs again.


The first question is whether or not we pre-generate (and/or cache) the LODs before the game starts, or create everything on-the-fly (as the terrain engine does). I think on-the-fly generation is very much possible and our best option - there's no need to delete a cache when changing mods, and when we use many different pages at different levels of detail depending on where the player is then generating all these in advance would be prohibitively expensive anyway.

A basic implementation shouldn't be too hard to do by reusing much of the code / concepts of the distant terrain code (e.g. the Quad Tree, the View and the way preloading works). Much of the speed-up compared to loading cells normally will come from being able to ignore physics, NPCs, particles, and smaller objects. Also running an optimizer pass that merges objects (which we can't normally do because normal objects move all the time) should help a lot.

Ignoring small objects presents a dilemma: we need to know the size of the mesh before we load it, but we have to load it to know its size. So basically we'll have to load everything anyway. To mitigate the impact of this I propose that we store in cache a 'mesh file -> bounding box/size' file so we can avoid loading all meshes everytime the game starts.

Mesh LOD

Ok, LODs. The first 'solution' here is to not simplify anything (other than running the optimizer pass to merge nodes). After all, Morrowind is an old game and its meshes are already low-poly by today's standards. So before implementing anything crazy I'd suggest to try using things as they are.

That said for very high view distances and/or modded high-poly meshes some form of simplification is still going to be necessary. This will be difficult because some meshes can't be simplified further and we also don't know which meshes are supposed to be smooth (organic) or solid. I suspect this will involve lots of trial, error and heuristics. But it should absolutely be our goal that things like whitelists/blacklists/manual parameters for certain meshes are not going to be necessary and everything will just work out of the box. In any case, osg provides a Simplifier class which looks to be a half-decent starting point (note you have to apply osg commit a13b66135f826688acb860fc7f0f0c911435645d to make it work).

An alternative simplification method is to use impostors (prerendered 2d billboards). A great example of this technique is Ogre3D paged geometry. This method tends to work great for trees, foliage, and rocks, but not so well for other meshes. It should be possible for some heuristics to select the simplification method to use (e.g. if a model is Z-rotionally invariant and uses transparency, it's probably a tree/foliage).

Note OSG provides an Impostor class but no way to batch them, so we'd have to roll our own.

Texture LOD

As with the above, how much LOD we really need remains to be seen. But I suspect textures might be more of a problem than vertices. A potential optimization is texture atlasing (in other words, merging several textures into one texture, then using UV coordinates to select the correct texture for each mesh).

The usual issue with texture atlasing is that we don't know which textures are going to be used together and for how long. But for distant objects, we do know - they rarely move relative to the player - so using texture atlasing might be a good technique here. We can build one or more atlas per quad tree node. The only problem is handling 'repeat' textures/UVs. I wonder how MGE handles those?

osg includes a TextureAtlasVisitor. I briefly tried it once and it didn't work correctly, but I may have done something wrong. Also there was an issue with handling of Drawables which has been fixed (but likely still needs to be applied to your osg install).

Enable / disable distant objects

Some people have asked if OpenMW is going to have the same issue as in MGE where objects dynamically enabled/disabled by scripts at runtime are not going to have their distant LOD updated. In theory this can be implemented with a little bit of effort, if we can accept a minor stuttering in FPS as LODs are rebuilt. It should be noted, though, that issues could remain if the relevant script only runs when the cell is loaded (as with the Strongholds, AFAIK). In this case the object would be initially enabled until you visit the cell. This can be easily fixed by modders, i.e. add a script that disables the object on the start of the game.

Re: Distant objects

Posted: 06 Sep 2017, 22:31
by raevol
Thank you for this! Fingers crossed someone can take a look at it.

Re: Distant objects

Posted: 07 Sep 2017, 08:35
by psi29a
Thank you for the guiding light scrawl. :)

Re: Distant objects

Posted: 08 Sep 2017, 01:03
by Deltaxus
I don't know much about graphics programming, would one of those approaches eventually allow to load a map the size of Tamriel seamlessly
into one worldspace ?

Re: Distant objects

Posted: 03 Nov 2017, 16:17
by ajira2
Distant objects would be amazing. Thank you scrawl.

Re: Distant objects

Posted: 13 Jun 2018, 15:33
by Starsheep
if this can be of any help design or inspiration-wise, there's this tool made for Skyrim (and special edition) that generates LODs based on a given load order.


Basically it loads all the .esp in a given load order using the xedit framework then comes two passes:
1) combine separate textures into lod textures
2)for every world, it gathers all the references of cells, meshes etc.. make atlases for textures, filter stuff by tag and predefined rules and add the new form ids in a new esp, along with subsequent data files, that needs to be loaded at the very end.

Attaching to that post two log files from the generation process done on a heavy load order. Some mods have their associated billboards, some others don't.

Re: Distant objects

Posted: 13 Jun 2018, 17:53
by akortunov
About osgUtil::Simplifier: it provides a quite good results (but not ideal). Here are screenshots with 50% detail.
Simplification takes a lot of time, so I am not sure if we will manage to do it on the fly.

Re: Distant objects

Posted: 13 Jun 2018, 20:12
by magamo
Those look to be pretty ideal to me, especially if these simplified meshes were only shown at greater distances, with the full object being used for things closer. This looks no worse than MGE's 50% simplification to me.

Re: Distant objects

Posted: 13 Jun 2018, 20:27
by psi29a
akortunov wrote: 13 Jun 2018, 17:53 About osgUtil::Simplifier: it provides a quite good results (but not ideal). Here are screenshots with 50% detail.
Simplification takes a lot of time, so I am not sure if we will manage to do it on the fly.
Is this something we can do in an apart thread, caching things to disk as we go and popping them into existence once created? This is non-blocking and pretty much how things are done in other games engines like UnrealEngine which will stream in the simple LOD first then complex afterwards... you can see the objects detail level going from low to high right in front of you when you first enter a scene. It's not that annoying.

Once in the scene however, when wondering around the rest will all be loaded in the background with the idea that you'll be moving slower than the thread can create simple LOD objects.

Re: Distant objects

Posted: 13 Jun 2018, 21:42
by Chris
Ideally the model(s) themselves would have LOD meshes premade, and can be just used. Like how DDS files can contain premade mipmaps. Generating and caching them to disk at load time can work for models that don't have premade LODs, but this will slow down start up.

You can stream in models from low to high detail, ending at the highest detail necessary for its given distance, with the idea being a model will first appear in the distance and slowly move toward the camera. And for cases where that assumption breaks, you see a low detail version of the mesh before the higher detail versions get loaded in to replace it (though even then it's still a benefit since you can load in the low detail version faster, if not have the lowest-detail version of all meshes constantly cached, to avoid stuttering at run-time as you wait to load in new models).