How to use OpenGLSurface to draw 2D tiles?

Are you loading the map only once and then using the camera to move across the map? I need to understand where you are experiencing the lag.

Will also do some testing on this side to see where things might possibly be improved.

If you can put the tiles in a single displaylist it’ll be faster. I’m not sure sure but I think in the demo the 100x100 tiles are being drawn with individual calls.

Yes, in the demo each tile is instantiated as a seperate model using its own display list. This is obviously proved not to be effective.

Anton, how urgent do you need to implement this. If you can give me a few days I can put together an X3TileMap class for you to make rendering of large maps more efficient using a single display list like Will suggested.

It is not really urgent. I really appreciate your help; an X3TileMap class would really make life easier for 2D game developers like me.

And to your previous post, yes, I load the whole map and then use the camera to move. The lag occurs when I move the camera, as it is much smoother when there are 20 tiles compared to 200. It also takes several seconds to draw the map.

Thank you for all the help!

Cool, I’m going to make two changes. The first change will be to see if I can change the code to use a single display list when you draw via X3Graphics. This might be good enough to increase performance to an acceptable level without requiring changes on your side. But since tiling will always be important I will also look into a proper X3TileMap class to make management of tile maps easier.

Alwyn, although I will admit I only have a basic understanding of OpenGL, from looking at the code in X3 I think the place where you can make the largest performance optimization is in the X3_RenderScene method. It simply loops through all of the models in the scene and renders them without checking to see if they are visible to the camera. If you modified the code so that only visible models are rendered, I believe there will be a large performance boost.

Your observation relates to the first optimization I’m thinking of doing, packing all tiles into a single model resulting in one display list. Your observation is correct that X3 currently loops through 200x200=40,000 tiles/models. Packing them into a single model should shift the processing from CPU to GPU reducing the latency. By letting OpenGL (or the hardware) take care of the camera clipping things should show decent improvements. Will post more info here for you guys as soon as I have some results.

UPDATE:

I’m getting some massive performance gains in preliminary tests using Will’s idea of using a single display list, combined with Jason’s observation that the X3_Render methods loops through too many models.

As soon as I have an update ready that properly fixes the performance issues with large tile maps, I’ll post a link to the download.

Just a heads up, to get the nice performance I had to compromise on creating an individual model for each object drawn onto the X3Canvas (all rendering now done as a single X3Model). The impact of this is that the X3Canvas.ModelMouseDown is no longer relevant when you use X3Graphics for drawing, since all objects are part of the same X3Model.

You will however still be able to select individually drawn objects with the X3Canvas.PolygonMouseDown event. Polygons however doesn’t have the convenient (Rotate and Scale) methods (YET) that Models have. I plan to add such methods in time.

Anton, would it please be possible for you to test your project with the latest X3Core module:

https://github.com/alwyn1024/x3-core/tree/master/modules

and check if thinks are now running better?

I still do have a few things I will update in the next few days, but you should see a slight improvement with the latest X3Core module.

Ok. I tested the code, however its performance hasn’t improved. It still takes about 10 seconds to render 900 tiles. (a 30 by 30 map). Here is the main method I use to render the map:

Method Name: drawTile
Method Parameters: tileset as Picture ,sourceX as integer, sourceY as integer, destX as integer, destY as integer

Code:

dim tile as new picture(32, 32)
tile.graphics.DrawPicture(tileset, 0,0,32,32,sourceX ,sourceY,32,32)
dim tileTexture as new X3Core.X3Texture(tile)

Dim g As X3Core.X3Graphics
Self.MouseCursor = System.Cursors.StandardPointer
g = TileCanvas.Graphics
g.DrawTexture tileTexture, destX, destY

I hope this helps.

In your drawTile method Anton, try commenting out all the code and use this code instead:

Sub drawTile(tileset as Picture ,sourceX as integer, sourceY as integer, destX as integer, destY as integer)
  TileCanvas.Graphics.DrawPicture tileset, destX, destY, 32, 32,sourceX, sourceY, 32, 32
End Sub

If you are using the latest X3Core, it will automatically only create a single texture to use for drawing, even if you call Graphics.DrawPicture multiple times. This should save a lot of memory and processing time.

If the above does work, you might even consider to call

TileCanvas.Graphics.DrawPicture....

directly instead of using a drawTile subroutine, to save some processing overhead on calling a subroutine each time,

In hindsight, the problem with the first method is that a separate texture object is created for each and every tile. You can imagine that 40,000 objects will consume a lot of memory and processing.

I’ve now fixed up the X3Core to be intelligent enough to automatically reuse textures and rendering everything to a single model.

So if you use the recommended change it should reduce the number of textures from 40,000 to 1, and the number of X3Model objects used in the background from 40,000 to 1.

This improvement only takes effect if you use the X3Graphics.DrawPicture instead of the native Graphics.DrawPicture method though.

You should see the performance gains you are looking for by making the recommended change to your code.

Ok. The code works great. There is no longer any lag, but I think there may be a small problem.

I call drawTile: drawTile( forest, 1332, 2032, x * 32, y * 32) with drawTile containing the X3Graphics DrawPicture. However, the graphics doesn’t render properly. The tile looks like it has been resized from a non-32 by 32 portion of the tileset.

I tested different values for sourceY, and when sourceY is around 532, the code works fine. For example: drawTile(forest, 1232, 0, x * 32, y * 32) draws the tile correctly, however when sourceY is around 20*32, the code renders a weird, resized tile.

Besides that, your class works superbly!

Great to hear the lag is now gone, so we are at least making good progress with the performance issues.

Anton, I’ll set up a test project on this side to see if I can reproduce the issue. It should be easy to find and fix the problem as soon as I can reproduce it. I will get back to you asap on this one.

On a side note, I’ve added a new boolean property “RenderSingleModel” to the X3Graphics class.

RenderSingleModel = True -> All tiles are rendered to a single model and textures are reused where possible (the default setting).
RenderSingleModel = False -> Each DrawPicture creates a new separate model and texture.

I’ve decided to support both modes because they both have their pros and cons. Just mentioning it so that people are aware of it.

[quote=128029:@Anton Luk]Ok. The code works great. There is no longer any lag, but I think there may be a small problem.

I call drawTile: drawTile( forest, 1332, 2032, x * 32, y * 32) with drawTile containing the X3Graphics DrawPicture. However, the graphics doesn’t render properly. The tile looks like it has been resized from a non-32 by 32 portion of the tileset.

I tested different values for sourceY, and when sourceY is around 532, the code works fine. For example: drawTile(forest, 1232, 0, x * 32, y * 32) draws the tile correctly, however when sourceY is around 20*32, the code renders a weird, resized tile.

Besides that, your class works superbly![/quote]

I looked at the code and found and fixed a bug in X3Graphics.FillRect(), but that wouldn’t be the cause of the stretching problem you are getting.

Is there any chance that you would be willing to share your project privately with me Anton, so that I can take a look at the problem. I will treat your project confidentially and remove it after resolving the issue. I am just unable to reproduce the problem on this side.