Generic Paging System for JME (Committed)

Some quick updates:

This is available for commiters to tear apart and turn it into something as useful as possible for others once everyone is happy with it. I’d love to see someone put together a JME terrain test using this (I really wish I could, however… I couldn’t seem to wrap my head around what was what in the available terrain test files).

Until I’ve better commented the code, see the pagingTest project (and the 5 or so test files) for usage. It is fairly straight forward, however… when starting to use dependent delegators, It’s important the the parent delegator is registered (with either another delegator or the paging manager itself) BEOFRE adding it’s dependent.

Delegator 1 created, then registered with the manager.
Delegator 2 created, then registered with Delegator 1
Delegator 3 created, then registered with Delegator 1 (yes, a second dependent for Delegator ONE)
Delegator 4 created, then registered with Delegator 3

Hope this makes enough sense…

_________________________________________________ OP:

Soooo… i’ve started working on a generic paging system for JME.

First, some highlights of what it will do once complete, then a quick vid of it handling ManagedNode(s).

It consists of (currently):

  1. A Control that handles the paging, which is initialized by passing it the Executor your app uses and the default camera.
  2. A wrapper class for Nodes, Geometries & Meshes respectively called ManagedNode, ManagedGeometry & ManagedMesh

It currently handles:

  1. Loading objects in and out of your scene based on the defined max distance set per node, geometry, mesh.
  2. Single object add, single object remove per frame.
  3. Creating defined custom meshes and caching recently used meshes for later use.

Quick vid & then a list of features that I would like to see added:
Vid scene consists of 19,200 (gah… I r can do teh math) individual static nodes being managed, each third of the total has a diferrent max distance set.

[video]http://youtu.be/QpPzcWXwH3E[/video]

List of features I would like o see added… but… need some assistance with:

  1. Object Fading… I’d love to see this, however it would require shader upates and I don’t know how agreeable people would be to this. Though, if anyone is interested in exploring possible ways of accomplishing this, please do let me know.
  2. Automatic Batching… based on user defines. (yes,this is why I was asking about it)
  3. Light handling (at a minimum for omni directional and spots. This would require input from others… too many ideas to list at the moment).
    more… but… this is a good start.

Thoughts? ideas?

Here is a vid of it used for managed custom meshes:

[video]http://youtu.be/4T_gdAuhWP4[/video]

Test of delegator, LOD and a dependent delegator: (White crap is grass)

[video]http://youtu.be/yFVGJhcjrak[/video]

Test of different types of dependent delegators + object fading:

[video]http://youtu.be/BiGAMJss8AE[/video]

13 Likes

Nice!

This will be a great way to replace terraingrid

1 Like
@Sploreg said:
Nice!
This will be a great way to replace terraingrid


I could use some input on how to handle this! I've been kicking around a few ideas and I think the most generic way of dealing with complex systems (this isn't completely thought through, however... this seems to be the best way of handling them without requiring any significant changes to existing systems) would be to create something like a:

ConcurrentHashMap <Callable, String> to register custom meshes, allowing the user to completely control how their objects are created.

The bigger issue is how to define when the objects are created. Static nodes can be pre-loaded and polled for user defined information (wrapped into the ManagedNode class). Any thoughts on how to best handle this? Obviously it will require some way to possibly define any number of attributes: (I'll use terrain as a for instance)

Tile size?
Max # of visible tiles (12x12? etc)
LoD distances maybe?
Noise fiter options?

Managing them is slightly easier... abstracted methods through the ManagedMesh for common things like changing LOD, etc, etc.

Speaking of LOD. I'm a little shakey on what support JME has for models loaded as an asset... actually... I'm a little shakey on how BlenderToOGRE handles these to begin with.
1 Like

Kewl, great you started work on this, always better to talk about code in the end :wink: Geometry can have LOD levels which is just basically a different set of index buffers for one mesh mostly. The (extended) ogre importer creates them by itself, I don’t think theres much other support for this actually…

1 Like

From what I have seen in the video, the pages diappear when you climb up, so this is paging in 3 dimensions? Cause in a game you sometimes have the desire to not load unload on great heights but just let lod work

Is this a completely pregenerated scene where just the nodes are managed therafter, or is this a real streaming of “tiles” based on lod? I nodes appearing first are the ones with the least details, and when you get closer the nodes with more details are loaded in, or is it just the whole “tile” and therafter lod works for activating the different nodes? I’m irritated because you wrote about a scene with 19200, but from what I see this is like a 5x5 field with 3 boxes in each.

Well terrain will be one of the objects there that cares on the size of your nodes, since it tiles together and should touch at edges. So for it it matters that is is a power of 2 +1 (257, 513, 1025 etc).



That aside, it can automatically seam itself with LOD using the NeighbourFinder interface that it has. It’s just 4 methods: getLeftNeighbour, getRight, top, down.



Now for LOD. Terrain will do this automatically obviously, but you don’t want to load 60 tiles of terrain unless you hate having spare memory. To help with this you can load in only every N points of the heightmap (N being a power of 2), and then scaling the terrain.

For example if I load in every 2nd point, then the terrain will end up being half the width and height that it normally is (but has way less points). To balance that, I would scale it by 2 in X and Z directions.

So you can do this for terrain as you get farther out, load in smaller and smaller tiles, that then get scaled larger and larger to compensate. Sample every 4 height points when far away, scale the terrain by 4 to fit. Sample every 8 points when really far away, scale the terrain by 8 to fit etc.

I could add something like this into quad, at least for understanding to take every N point and scale accordingly. That quad would have to be swapped out each time when the, lets call it major LOD, changes. And the entire heightmap would have to be passed in. Not a huge amount of memory, but it really isn’t needed and the important points could be streamed in themselves instead.

Btw, if you want you can set up a project in the contrib repo already, it doesn’t need to be compiled to a plugin or anything yet, just so that everybody can access the code and help out / get some insight etc. I’d totally get it if you think you’ll still refactor too much to add the hassle of version management on top tho ^^

Tired… so bare with me on the answers to peoples posts.


@ghoust said:
From what I have seen in the video, the pages diappear when you climb up, so this is paging in 3 dimensions? Cause in a game you sometimes have the desire to not load unload on great heights but just let lod work


Yes and yes.... I think you should be able to disable y axis paging.

@ghoust said:
Is this a completely pregenerated scene where just the nodes are managed therafter, or is this a real streaming of "tiles" based on lod? I nodes appearing first are the ones with the least details, and when you get closer the nodes with more details are loaded in, or is it just the whole "tile" and therafter lod works for activating the different nodes?


I hope I'm able to answer this fully... LOD is stored within a mesh, different buffers are loaded depending on distance. The paging system will handle individual objects, swap out LOD based on defined distances per object. These objects can be whatever you decide they are (a single mesh or geometry... or a node containing many of these). The reason the paging system will handle objects this way (though, I'm not sure what other way one would consider) is, terrain tiles would be handled completely differently than say, plants/trees, which would be handled completely differently than player geometries in a multiplayer game, which would be handled completely differently than repositioning a defined number of omni-directional lights based on distance/camera direction (if you follow).

@ghoust said:
I'm irritated because you wrote about a scene with 19200, but from what I see this is like a 5x5 field with 3 boxes in each.


That's is what the paging system will do... It will help manage your rendered scene by adding/removing objects based on user defines per object/object type, help manage LOD and provide a way to create new/cache frequently used dynamically created meshes hopefully based on the end-users choices while playing your game. The system needs to account for as many possible scenarios as it can (including precreated scenes, as well as dynamically created scenes).

In the case of the video scene, you are seeing a 5x5(ish) field of blue boxes which met the distance requirement (out of the 6400 blue boxes being managed) and were loaded into the scene. You're also seeing a 4x4(ish) field of green boxes which met a different distance requirement (out of the 6400 green boxes being managed) and a smaller number of red boxes (out of 6400 being managed) that met another distance requirement.

The video is not showing LOD handling at all, as I wanted to discuss this with people prior to blindly deciding on the best way of handling this. I imagine that LOD handling will be optional, seeing as not all objects will support LOD and some instances (like what @Sploreg was discussing with terrain tiles) where the objects will manage their own LOD.
1 Like
@Sploreg said:
Well terrain will be one of the objects there that cares on the size of your nodes, since it tiles together and should touch at edges. So for it it matters that is is a power of 2 +1 (257, 513, 1025 etc).

That aside, it can automatically seam itself with LOD using the NeighbourFinder interface that it has. It's just 4 methods: getLeftNeighbour, getRight, top, down.


Before going further, I just wanted to ask about this. I thought the stiching was really cool (watching it rendered as a wire frame), however... there are still visible gaps between tiles in some instances (couldn't tell you the when and why). Is there a reason stiching is being used as apposed to the skirting approach that @pspeed had mentioned a while back?

Not that this matters much, but the idea worked wonderfully, never any gaps, no need to know anything about adjecent neighbours, etc, etc.
@Sploreg said:
Now for LOD. Terrain will do this automatically obviously, but you don't want to load 60 tiles of terrain unless you hate having spare memory. To help with this you can load in only every N points of the heightmap (N being a power of 2), and then scaling the terrain.
For example if I load in every 2nd point, then the terrain will end up being half the width and height that it normally is (but has way less points). To balance that, I would scale it by 2 in X and Z directions.
So you can do this for terrain as you get farther out, load in smaller and smaller tiles, that then get scaled larger and larger to compensate. Sample every 4 height points when far away, scale the terrain by 4 to fit. Sample every 8 points when really far away, scale the terrain by 8 to fit etc.
So you can do this for terrain as you get farther out, load in smaller and smaller tiles, that then get scaled larger and larger to compensate. Sample every 4 height points when far away, scale the terrain by 4 to fit. Sample every 8 points when really far away, scale the terrain by 8 to fit etc.
I could add something like this into quad, at least for understanding to take every N point and scale accordingly. That quad would have to be swapped out each time when the, lets call it major LOD, changes. And the entire heightmap would have to be passed in. Not a huge amount of memory, but it really isn’t needed and the important points could be streamed in themselves instead.


Ok... am I understanding this correctly... The heightmap is generated, however the buffer information is not being generated from the height map until needed? I assume this is due to needing to stitch between adjecent tiles?
@normen said:
Btw, if you want you can set up a project in the contrib repo already, it doesn't need to be compiled to a plugin or anything yet, just so that everybody can access the code and help out / get some insight etc. I'd totally get it if you think you'll still refactor too much to add the hassle of version management on top tho ^^


I would love to do this, however... there isn't a ton to share as of yet. I could do this now, or wait until I have a little bit more of the ManagedMesh together and then have a bit more to think about/work off of/consider/etc.

I also meant to mention that... eventually it would be great to have this support physics as well. At this point... I will be at a COMPLETE loss, as I haven't worked with it at all.

Quick breakdown of how I think the ManagedMesh (custom mesh management) will work: This is both for my benefit… and so I can get some feedback on the thought process.



Managed custom meshes will consist of a few things actually… here be the list and I’ll try to explain the why’s as I go:


  1. An Abstract ManagedMesh class that extends Mesh - this sets up common ways of handling LOD for those who want to use the functionality.
  2. A Tilables class that defines the properties of the custom mesh being managed - this defines things like tile size, max distance, LOD information, etc
  3. An Abstract ManagedMeshDelegator class - this became very import as I started discovering some of the pitfalls of trying to implement generic tile management. It forces a single method that is passed a Vector3f(tile location) and must return a ManagedMesh, allowing the user to handle all other aspects of the mesh generation (textures, materials, information relating to heightmap generation, whatever is needed basically).



    Here is a for instance that might help explain the need: A custom mesh containing all manner of vegetation that the user decided to handle in this fashoin:
  4. Load precreated coverage images to define placement
  5. Load multiple asset models as templates, read buffers and start appending the mesh buffers with multiple copies of each plant type in their specific location/rotation.
  6. Texture swaping based on game time
  7. Some other weird shit that no one could EVER account for.



    And another:

    Custom terrain the user built by:
  8. Loading basic topographical images for major land/water placement
  9. These are sectioned & modified by noise filtering that requires global setting + tile location
  10. Global Texture/Material information



    Anyways… you get the general idea. The only common factor between all scenarios I could think of was the tile location. Thus, the addition of the ManagedMeshDelegator class.



    So this all works out to something like this:



    [java]

    ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(2);



    PagingManager pm = new PagingManager(exec, cam);

    pm.setSpatial(rootNode);

    rootNode.addControl(pm);



    Tilable terrainTile = new Tilable("Terrain", 40f, 12);



    UserExtendedManagedMeshDelegator delegator = new UserExtendedManagedMeshDelegator(user_defined_info, etc, etc);



    pm.registerManagedMeshType("Terrain", terrainTile, delegator);



    pm.setEnabled(true);

    [/java]



    At this point (in the example above)… terrain tiles will now be created, loaded, unloaded, cached, destroyed, etc as defined… showing 12x12 tile (or those that meet the distance requirements of 12*40/2 as defined).
1 Like
@t0neg0d said:
however... there are still visible gaps between tiles in some instances (couldn't tell you the when and why).

There shouldn't be gaps if the terrain can find its proper neighbour. Terrain already does stitching for the individual terrain patches, and the new interface just lets those patches look over one more neighbour: to the next tile.

@t0neg0d said:
Ok… am I understanding this correctly… The heightmap is generated, however the buffer information is not being generated from the height map until needed? I assume this is due to needing to stitch between adjecent tiles?

In order to switch the LOD of the terrain quickly, just the index buffer is changed. So it is loaded with all detail of the terrain at the start, and adjusts the indices accordingly.
To really stream the LOD of terrain and to keep the fast index-based LOD switching, the terrain would have to be re-created and swapped out when it gets closer.
@Sploreg said:
There shouldn't be gaps if the terrain can find its proper neighbour. Terrain already does stitching for the individual terrain patches, and the new interface just lets those patches look over one more neighbour: to the next tile.


I see those also, rarely, but they do happen, even in the terrain fractal grid test, just keep flying and eventually they appear. The gaps aren't very large and goes away when the LOD switches. So it's only for certain distances they appear. But if you find such a place they always appear (so it's not time based). I haven't been able to record the coordinates to reproduce :/

TerrainGrid does not use this new interface for seaming. TerrainTestTile.java is the “new way” of doing it :slight_smile:

I would consider TerrainGrid @deprecated in this sense, too many issues with it and too restricted.

@t0neg0d said:
Is there a reason stiching is being used as apposed to the skirting approach that @pspeed had mentioned a while back?

Not that this matters much, but the idea worked wonderfully, never any gaps, no need to know anything about adjecent neighbours, etc, etc.


It is kind of magical. I think people who've never seen it in action will believe that there are some kind of artifacts or something. I wouldn't have believed it would work until I saw a terrain engine that used it and I never noticed the seams.

@t0neg0d +1 for the ability to specify my own Executor

@t0neg0d said:
I would love to do this, however... there isn't a ton to share as of yet. I could do this now, or wait until I have a little bit more of the ManagedMesh together and then have a bit more to think about/work off of/consider/etc.

I also meant to mention that... eventually it would be great to have this support physics as well. At this point... I will be at a COMPLETE loss, as I haven't worked with it at all.

Now that I see that the TerrainGrid is "deprecated" I'm even more interested in this. If you share your code we can all help out - so please do as normen suggests :)
For physics idk, haven't thought about it. But I guess it could be left to the "UserExtendedManagedMeshDelegator". When I (re)create a terrain tile I'll please ask the collisionshape factory to create a heightfield collision shape and add it to the physics space. Then remove it from the physics space when the tile gets swapped out.

Another thing I started thinking about was if/how to handle repartitioning of the tiles. Say that I start out with terrain tiles of 1025x1025 units. Then I want to change that to 513x513 units because it runs to slow (too many verts, not enough memory, whatever), what'll happen to the tile-coordinates, what if I cached the tiles as j3os on disk and so on. Just vague thoughts right now and maybe that's one problem not worth solving.

@jmaasing yea TerrainGrid just isn’t ideal because it puts a lot of restrictions on the user. I will add a warning in the java docs to steer some users away. I think some people just jump there immediately thinking that it is the only terrain implementation that is split up into tiles for frustum culling, which isn’t the case.



For the tile size problem you are talking about, I think that is most easily handled if a smart and small choice is made early on. Say 256x256 or 512x512 (of course depending on the game’s units of measurement), then you could scale out the number of visible tiles and allow a customizable view distance.

Always in the back of my mind with these infinite worlds is a level editor. Those darn level editors.

Sploreg said:
jmaasing yea TerrainGrid just isn't ideal because it puts a lot of restrictions on the user. I will add a warning in the java docs to steer some users away. I think some people just jump there immediately thinking that it is the only terrain implementation that is split up into tiles for frustum culling, which isn't the case.

For the tile size problem you are talking about, I think that is most easily handled if a smart and small choice is made early on. Say 256x256 or 512x512 (of course depending on the game's units of measurement), then you could scale out the number of visible tiles and allow a customizable view distance.
Always in the back of my mind with these infinite worlds is a level editor. Those darn level editors.


Yes, TerrainGrid is nice but limited. Like this example of small tiles, since it just loads 2x2 tiles using small tiles will make the viewing distance very short and there's nothing I can do about it on a more powerful machine. I think the TerrainGrid is more suited as a plug-in than in core (but I think a lot of things are like that, that's another discussion).
With a more generic paging system it would be nice to use say 64x64 wu on a tile (or whatever is workable) and page in 12x12 or something tiles. It seems easier to batch several tiles than try to repartition too large tiles after the fact.

As for level editors, for sure, the SDK and related tools are what makes jME different from other OSS-game-frameworks. Maybe the paging system could use some tool support in the SDK? Don't know what actually but maybe t0neg0d has some ideas, anyway I'd be happy to help out :)