Joining heightmaps

@jayfella said: I added a video so you can see what the hell I'm on about. The push to github just added the ability, it didn't add a working demo because jmonkey doesnt have any vegetation in the demo files for me to use. Just copy-paste the code i put in the previous post and it will work, but you'll need a tree model or something.
The video does look nice :D Anyways, the TileListener on github doesn't provide you with #tileLoadedThreaded method, neither does World etc.

The last code I see pushed is 7 hours ago

What holes are you seeing? Verify with wireframe view to see if they seam together. There are no bugs with the edge seaming in the LOD control, it has been used extensively. Unless you are jumping between several LOD levels, then there could be a gap, but that would only occur if you are setting different LOD scales for each control (but you should only be using the one control for all terrain). It is more likely if you are seeing a hole and then it fixes itself it is because the LOD control hasn’t run on that fully-detailed, just-loaded, terrain patch. Or you aren’t matching the neighbours up correctly via the neighbour finder.

@reveance sorry, i committed and forgot to push. It should be there now.

@Sploreg he means these:

@jayfella said: @reveance sorry, i committed and forgot to push. It should be there now.

@Sploreg he means these:


Alright thanks! And yeah, I just made a screenshot too :stuck_out_tongue:

Make sure the LOD finder is finding the correct neighbours. Check the wireframe to see what is happening. It could also be the noise generating different edges.

Its not, because when you move closer they disappear. They are only apparent when you are far away - i.e. when your LOD control works its magic.

@Sploreg, I’ve got handbrake and fraps open already, so i’ll show you an example of a hole in the seam that disappears when you move closer.

[video]http://www.youtube.com/watch?v=fNVYRHdkhSg[/video]

Okay then something is not finding the neighbours correctly.
If the breaks are just in the spaces between entire tiles, then it is for sure the neighbour finder because the LOD control is working on the individual patches on the terrain quads.

When the LOD control checks the LOD of a patch and how to seam the edges, it climbs up the quad tree and back down to find the neighbour’s LOD. That is what the neighbour finder does: links neighbours in separate terrain quad trees so each patch on the edges can always find a neighbour. If there is no neighbour, then it doesn’t seam the edges because it thinks it is the end of the map.

Hmm. Is this something my code should be fixing or is it an issue with the NeighborFinder? I can see a TerrainQuad#resetCachedNeighbours() method, and I tried executing that over all loaded tiles after a tile is added or removed, but it doesnt make any difference.

Neighbourfinder is just an interface that you will be implementing:
[java]public TerrainQuad getRightQuad(TerrainQuad center);

public TerrainQuad getLeftQuad(TerrainQuad center);

public TerrainQuad getTopQuad(TerrainQuad center);

public TerrainQuad getDownQuad(TerrainQuad center);[/java] 

In the test example (TerrainTestTile.java) it just stores 4 terrain quads and can easily look up what the neighbours are. You will have to probably provide a more sophisticated approach since the tiles are loaded dynamically.

If you are adding and removing terrain quads, then you want to call resetCachedNeighbours() on the edge quads. They cache the neighbours so they don’t have to crawl up the tree and back down again on the edges each time the LOD changes.

ugh… I can’t get it to work. I’ve tried using the neighborfinder, and I’m certain its finding the correct terrainQuads, and it just ends up worse than before. So I tried using your multiterrainLodControl as per TerrainTestTile.java and that made things even more worse. I dont care enough to spend anymore time on it today. I can’t figure it out for the life of me.

Hey, what I was talking about before with the cache, was not the same thing you were talking about. Sorry about the confusion. What I meant was that if the player moves across a tile border, and a new one is added to the scene graph ahead of the player, and then the player moves back across the border, the tile should not immediately disappear. If the player repeatedly crosses the border back and forth, the tile being added and removed from the scene graph will be very noticeable. The tile should have a set time limit to stay in the scene graph before being removed. If the game is more complex and there are numerous objects being added and removed as well as the tile, it could cause a frame drop on tile loading. You dont want the player to experience a frame drop multiple times when crossing a border. What if the player was in the middle of a battle near a border, and every time the cross it, the game fps drops? It would not be a good experience.

I hope i’m being clear, if you don’t understand I will try to clear it up.

Also, the directory “./world/” should be able to be modified from code. It is a simple change in the ImageBasedWorld and NoiseBasedWorld in the getTerrainChunk overrided method. Just make a variable for the directory name and a method to set it.

No, I get you. Well that’s kind of the problem with creating a “one size fits all” setup. This implementation removes the tiles before adding the new ones deliberately to keep the triangle and object count down, that’s all, and the cache was changed to pre-load the skirt tiles in advance instead of just holding onto the tile history, but in all honesty the code I wrote isnt complicated to read or understand, I guess you could modify it to your requirements with relative ease.

The tile you’ve just left shouldn’t be dropped from the scene anyway because its still within the viewdistance, it should only actually disappear when it’s greater than viewdistance tiles away…

@monkeychops said: The tile you've just left shouldn't be dropped from the scene anyway because its still within the viewdistance, it should only actually disappear when it's greater than viewdistance tiles away...

But the tiles do disappear when moving repeatedly across a boundary of a tile. I’m sure they are still kept in the cache, but they are being physically removed from the scene graph.

Another question. I’m assuming having physics heightmap collision is the same with this as with the TerrainGrid: in the tileLoaded() in the TileListener create a heightmap physics object for the terrain and place it in the physics space. But is this method called when the tile is physically added to the scene graph, or just when it is loaded into the cache? Can I have some explanations of what each of the methods is used for and when it is called in the TileListener?

Thanks

Yes, they get removed because they are no longer part of the view distance. That’s essentially what the view distance is there for. If we allow tiles to stay in the scene, we alter the graphical capability of the game. Your PC may be able to handle 5 tiles in each direction, by my poor laptop may not. If your game decides that a tile should stay in the scene for some reason or another, you increase the average triangle count significantly as a result, and it becomes unplayable in the situations that you deem appropriate to keep that tile in the scene. Just be aware of that whilst reading below.

The tiles extend TerrainQuad, and so inherit all of the cool stuff they can do.
The TileListener#TileLoaded and TileListener#TileUnloaded events are called directly before they are added or removed from the scene. You can return false to these events to cancel them if you wish, so as you stated, you didnt want to unload a tile - you would cancel the TileUnloaded event by returning false.

Tiles are distinguised by their position, and you can work out if something is in a tile like so:

[java]
// just some data to use as an example
Vector3f enemyPositoin = new Vector3f(199, 20, 2332);

int tileX = (int)enemyPosition.getX() >> bitshift;
int tileZ = (int)enemyPosition.getZ() >> bitshift;

// this is the tile location the enemy is in.
TerrainLocation enemyChunk = new TerrainLocation(tileX, tileZ);
[/java]

Now we know this, we can do the same in the TileUnloaded event.
[java]
public boolean TileUnloaded(TerrainChunk terrainChunk)
{

int tileX = (int)terrainChunk.getLocalTranslation().getX() >> bitshift;
int tileZ = (int)terrainChunk.getLocalTranslation().getZ() >> bitshift;

// this is the tile location of the tile that will unload.
TerrainLocation tLoc = new TerrainLocation(tileX, tileZ);

if (enemyChunk.equals(tLoc)
{
    // the enemy is in this chunk, and it wants to unload, 
    // so cancel the tile unload event.
    return false;
}

return true;

}
[/java]

The tile will attempt to remove again when you move out of the chunk you are in, so you dont necesarrily need to clean up after yourself when you cancel it.

In addition, its also worth mentioning that there is also a TileListener#tileLoadedThreaded method, that allows you to load models, do some math, modify the tile as you please, add to it, whatever - in the thread that loads the tiles, rather than in the GL thread - which may cause a noticable delay in your game.

The threadpool in the World.java class determines how many threads to use. This threadpool is used to load tiles as well as determine the LOD of each loaded tile, so theoretically (depending on whether there are any threads available) each tile is loaded in a seperate thread - so although doing very intensive tasks wont delay the loading of other tiles (which is the ideal situation), it will delay the loading of that tile specifically.

1 Like

Thanks for that explanation!

I think I will have arraylists of the game objects that are in each chunk, and update them when an entity moves across a border, and use that list to unload all the entities in a chunk, because looping through every enemy when a tile unloads does not seem very performance friendly. Is this a good way to handle this?

@8Keep123 said: Thanks for that explanation!

I think I will have arraylists of the game objects that are in each chunk, and update them when an entity moves across a border, and use that list to unload all the entities in a chunk, because looping through every enemy when a tile unloads does not seem very performance friendly. Is this a good way to handle this?


Eventually you will want to query the scene for nearby enemies and objects, and for that you will want something like an Oct-tree, Quad-tree, or R-tree. But to just get it working initially, a list of objects is a start.