#X3D V3.2 utf8
PROFILE Interactive
LOD {
children [
Inline { url "cube_close.glb" } # used when distance < 10
Inline { url "cube.glb" } # used when distance between 10 and 100
Inline { url "cube_far.glb" } # used when distance > 100
]
range [ 10 100 ]
}
However, no matter how far I go (up to some 400m) the model doesn’t update. I’ve made no code assuming the LOD is handled by the engine without my intervention. On another world I have used 3 completely different models but the game was crashing. Here, it doesn’t crash, but is not using the different models at all.
Is there any switch or property that I’m missing? It’s the very basic world from your examples, just the cube is added by me (and default song volume put to zero, not my taste).
Oh, I see I missed one important information in my instructions how to use it: you need to set ProcessEvents property to true on the TCastleScene instance (that has the model with X3D file with LOD). You can do this in the editor also, you can even select multiple scenes with Shift/Ctrl and toggle ProcessEvents for the whole selection.
I’ve made a simple demo in examples/viewport_and_scenes/level_of_detail_demo, attaching also below. With ProcessEvents = true in “just works”. I documented in README in that example the need to turn on ProcessEvents – hopefully others that see this demo will have it easier
I pushed a small improvement to this example, also used TCastleScene.Cache = true to optimize loading (the TCastleScene.Cache is a feature independent from LOD – it’s useful when many TCastleScene refer to the same URL but for some reason one cannot use TCastleTransformReference, e.g. here TCastleTransformReference would prevent LOD being different in each scene).
Although I knew the Cashed, I wasn’t sure if TCastleTransformReference can be affected by LOD, thanks for clarifying it. I quickly made one “clone” linked by reference, and it indeed changes when the original object gets far, no matter how close the “clone” is.
So, as I understand
We use TCastleTransformReference if we want to link the referenced transform’s current geometry, kind of instantiating the geometry - not the Scene object itself. Until now, I thought about the original object as a Class and the linked ones as Objects, but I was wrong. Makes sense to me (well, I think what I just wrote only I can understand )
And if I have many bushes (obviously with LOD) I have to make separate Scene objects, linked just by the model URL (LOD x3dv file in this case), so they share the model with all geometries not just current geometry used by the original one.
It changes a bit my understanding of how CGE works. Still a lot to learn.
Do I need to do Cashed = true for each of the duplicates? It looks like it’s unnecessary from a quick test. Even when I un-Exists the first object (which has Cashed=true) the duplicate objects still work even with Cashed=false. So I guess its by reference counter. I didn’t dig into the source code there yet.
On the same time instances TCastleTransformReference disappear when referenced object change Exists or Visible.
This separation of how the TCastleSceneReference works has benefits. When I have many spoons on a table inside a house, then only 1 need to be actual TCastleScene. And when player moves out of the house they all change geometry at once. So every house in my village can use just 1 TCastleScene per house (for spoons at least), and the whole village shares 1 Cashed copy of the 3 LOD geometries. Very efficient.
The “right” way to think about how TCastleTransformReference works (that works for my mind, and it is also how it really works internally) is that all TCastleTransformReference are just additional pointers to the same object (defined in TCastleTransformReference.Reference). So every change to the underlying object (and any children of that object – as you can refer to a hierarchy of TCastleTransform/TCastleScene instances, with behaviors, colliders etc.) is reflected in all occurrences of TCastleTransformReference , because underneath there’s really only one object.
This implies both some strengths and weaknesses of TCastleTransformReference
TCastleTransformReference really makes a significant memory saving. Even if you have a million references to a TCastleScene, there’s actually only 1 TCastleScene scene in memory, just rendered a million times in each frame. This is very efficient, for memory, for GPU resources, which in turn makes it also very fast to render.
On the other hand, because there is really only oneTCastleScene (just referenced a million times by the TCastleTransformReference), then all of the references must really, necessarily, show the same thing. Everything that is “stored” in TCastleScene, not recalculated at runtime, is just stored once. This implies all properties of the TCastleScene are the same.
This also implies e.g. current moment in the animation is the same, and current LOD shown are the same.
But, here is an addendum: it can actually be improved in some cases when the rendering moment of something could decide what is rendered. This reasoning is applicable to the LOD. So, it’s actually a bug that LOD doesn’t cooperate right now with TCastleTransformReference, and it’s something we could improve. TODO: I want to look at it soon, hopefully today. I have too many tasks on my head If I manage to do this, then LOD + TCastleTransformReference may be OK
Note that TCastleSceneCore.Cache is only an optimization. You will not see a different look / functionality of anything if you toggle the Cache value. It only implies that loading may be a bit faster / slower.
And to utilize the cache best, you should set Cache = true on all occurrences. If you don’t → nothing bad will happen, but the instances with Cache = false are just loaded without looking at cache, thus are a bit slower to load. I guess this happened in your experiments, you just didn’t observe the loading speed increase, because it was too small to notice.
You can check out the example examples/viewport_and_scenes/occlusion_culling that sets up a small city with lots of buildings repeated – it uses Cache = true for all objects. With Cache = false, it would load much slower.
Now, the valid question you may be wondering is “why don’t you just set Cache = true by default on all TCastleScene instances? or even, just behave as it Cache = true, and not even show us this property?” . The answer is: using Cache = true on an object that has only one occurrence (references to it, using TCastleTransformReference, don’t count here) will actually make it a bit slower to load. Merely using cache means we spend a small time recording the object to cache – which is a time wasted, if nothing else will actually use this cache. And this is also a TODO, which we could fix one day. Then Cache property will just disappear, and both use-cases will be efficient automatically. Right now, you need to “help” our engine:
set Cache = true if you know the given TCastleScene.URL will be used in many TCastleScene instances.
leave Cache = false in other cases.
Hope this helps to understand how it all works, and how it could be better in the future too