Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Render Part of a Mesh

edited August 2013 in Questions Posts: 391

Recently it was pointed out to me that there are frame rate issues when using large maps in my RPGenerator project. I am now looking for an efficient way to render only the part of the mesh that would show up on screen. I have tried the following so far:

  1. Position buffer (but that seems to only apply to the mesh texture and not the mesh rectangles)

  2. Clip (seems as though it clips off screen by default so it didn't help)

  3. Generating a smaller mesh and changing it every time the player moves (this lowered frame rate while standing still but made it very choppy on every move)

What is the best way to go about limiting how much of a mesh should be drawn?

Tagged:

Comments

  • Posts: 730

    Not sure how you are implementing your mesh. The way I've done it in the past is to check to see if the rectangle from the mesh (and I guess triangle though I've not tried) is on screen. If not, then don't add it to the mesh (I think this is your part 1?)

    This demo shows shows the the difference in speed between drawing all the mesh elements and just those within a portion of the screen


    -- meshdemo -- Use this function to perform your initial setup function setup() m=mesh() img=readImage("Cargo Bot:Crate Blue 3") m.texture=img tile={} globalx=0 globaly=0 size=150 w=10 border=320 for i=1,size do tile[i]={} for j=1,size do tile[i][j]=math.random(2) end end parameter.integer("switch",0,1,0) end -- This function gets called once every frame function draw() -- This sets a dark background color background(40, 40, 50) globalx = globalx + math.sin(0.5*ElapsedTime) m:clear() for i=1,size do for j=1,size do if tile[i][j]==1 then if switch==1 then if w*i-globalx>border and w*i-globalx<WIDTH-border and w*j-globaly>border and w*j-globaly<HEIGHT-border then local bid=m:addRect(w*i-globalx,w*j,w,w) m:setRectTex(bid,0,0,1,1) end else local bid=m:addRect(w*i-globalx,w*j,w,w) m:setRectTex(bid,0,0,1,1) end end end end m:draw() end
  • IgnatzIgnatz Mod
    Posts: 5,396

    @Slashin8r - as I recall, you have made things easier for yourself by using a standard tile size and making each one a separate mesh, in a 2D table of tiles. If that's the case, it should be pretty simple to figure out which tiles are on screen at any time.

  • edited August 2013 Posts: 157

    Is this a 2D or 3D engine?

    Rather than using a single mesh, can you render tiles on the screen using tiles? Games like Neverwinter Nights (the original one, not the MMO) actually use 3D "tiles" to build the scene, only drawing the elements necessary for the part of the world that's visible to the user.

    I've been working on a 2D game engine for Windows, and I render the world using a grid of tiles. When you render the scene, you just draw the cells that are actually in your view area.

    The advantage of both systems is that you can assemble your world "Lego Brick" style, and it's easy to put together large, complex worlds that way.

  • edited August 2013 Posts: 391

    @West, your solution is my #3. It works well for when the player isn't moving, but recreating the mesh (which is really more like 50 meshes give or take depending on how many tile textures are being used) causes each player movement to lag, so movement is very choppy.

    @Ignatz, yep, it is very easy for me to figure out which tiles should be rendered, the problem is that my maps are generated as a whole, so how do I take those generated meshes and pick out the tiles that I need to be rendered?

    @tomxp411, it is a 2D tile map. Lego brick style would explain exactly how it is currently configured. It consists of multiple map layers (floor, animated tiles, objects, decoration objects, then events which are invisible).

    Here is the process my project currently goes through. I read from 2D tables and create multiple meshes per table depending on the texture that needs to be used. The table of meshes it generates is stored for future use (so it doesn't have to recreate a map that's already been generated). The stored maps are the full maps including every tile, so using the standard mesh:draw() function would render everything, including tiles outside of view. Is it possible to edit the mesh:draw() function so I can make it only renders what is in view?

    If this isn't possible (or just not possible in the current version of Codea) that is ok. My own RPG I want to make I actually planned on making most maps as zones so even the world map would be split up into sections. It is even possible to make a zoned off world map seem seamless if the borders include tiles of the next zone and a teleport event is called before you can see the edge of the map. Small maps render quickly and doing something such as this would barely be noticeable loading wise.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Slashin8r - you either draw a (whole) mesh or you don't. So if your map is one big mesh, you have no choice. But if your map is made up of many meshes, you can test whether each of them appears on screen, using a function like this, that tests if two rectangles (in your case, the screen and one of the meshes) intersect.

    function intersect(a, b) 
      return (a.left <= b.right and b.left <= a.right and
              a.top <= b.bottom and b.top <= a.bottom)
    end
    
  • edited August 2013 Posts: 391

    @Ignatz, :( that's what I thought. I am actually thinking now to return to using sprites to render the maps. When I first started the project, sprites weren't as efficient, but now they may be capable of doing the task at hand. So instead of tables of meshes, I would make tables of sprites and then I would have full control over which ones get drawn. Do you think this would be a viable solution? I'm not sure how much more efficient batch sprite rendering is now, but from what I read on release notes, they should be comparable to using meshes.

    Edit: Thinking more about it, I think sprites may be the way to go for the tile maps. I will continue using meshes for the characters since they utilize shaders, but I don't think I have any reason to continue using meshes for the maps since Codea is no longer on v1.5.2, hehe.

    Thanks for the quick answers everyone. Hopefully I will be able to let you know how the rewrite works out some time tomorrow (if I can get it done that fast, lol).

  • IgnatzIgnatz Mod
    Posts: 5,396

    If you have a table of meshes, you can choose which items in that table get drawn.
    So I'm not sure what difference it would make if you changed to sprites. If you have one big mesh, then of course you have a problem).

  • Posts: 391

    It's both. I use a table of meshes, but the meshes are intertwined. So basically each mesh individually is roughly the same size. For example, if I have 4 desert tiles, one in each corner of the map, and the rest of the tiles are grass, this would only need 2 meshes, but the size of the meshes (coordinate wise) would be the same. Now take that example and make it a set of 50 meshes all mashed on top of one another, lol.

  • Posts: 2,161

    Have you considered making one giant mesh and rendering the whole lot in one go? The GPU does vertex culling which means that it already has the code for deciding which vertices to ignore. Doing it in the main Codea program means that you are, in effect, duplicating this facility but losing all the benefits of the GPU due to parallel processing. The GPU can decide for vertices all in one go, but the CPU needs to consider them one by one.

    So if you can rework it so that there are very few meshes, I think that you will see a significant increase in speed.

  • edited August 2013 Posts: 9

    On my end using one huge grid with one rect per tile for a 150*100 map works great, and I can effortlessly shift it about at 60fps. A 150*150 map however drops to around 40fps on my 2nd generation ipad, and 200*200 drops to around 20fps.

  • Posts: 391

    @Andrew_Stacey, I could make it use one mesh per tilesheet set which would maybe cut it down from 50 meshes to 10. I'd then have to rework some of the math, but it is definitely possible. The main problem of getting it down to one large mesh is that I need to control the drawing order (layers) and the image size limitation of Codea prevents me from cramming all my tilesheet sets into one image.

    @HeadCase, if I were to make a very large map of just one texture, I don't see any of the fps issues.

    So I definitely have some ideas on how to refine the meshes now. I think I will go both routes (meshes and sprites) and keep the functionality of both in my project just in case something changes in a future version of Codea.

  • Posts: 391

    Just finished the code to convert my meshes to sprites and all I have to say is:

    DON'T USE SPRITES! lol :P

    Using my original map which ran 60 fps on average was now running below 10 fps. Tried the large map which normally ran at 30 fps on average and it was running at about 3 fps.

    Good thing I made new functions for this test. I went back to meshes and even limited new mesh creation to full tilesheet sets. Now only 11 meshes are being created instead of 50. Most of my tilesheet sets are 512x512 or smaller so I can go even further and cram them together into larger images. I would need at least 4 separate tilesheet sets (floor/objects/animation, walls, roofs, and decor), but 4 meshes are better than 11 and much better than 50.

    After making the above change, the large map moved from 30 fps to about 40 fps on average.

  • Posts: 175

    Just a thought, what about the clip function? but a frame rate reduction, if any, will probably depend on how it handles drawing outside of the clipping rectangle :)

  • Posts: 391

    @XanDDemoX, tried that already (see #2 in first post). I think Codea clips beyond the screen by default.

  • edited August 2013 Posts: 580

    @Slashin8r: Are you recreating your mesh each time you need the background to change? If so, perhaps you could try modifying your approach to #3; instead of recreating your mesh each time, just rearrange it a little. What if you took the rects in the mesh that have scrolled off-screen, and reuse those (by repositioning them and re-setting the texture coordinates) for the upcoming portion of the screen? Your mesh would need to have 1 extra row and 1 extra column of tiles than what's visible on screen. It's possible this might work, but I haven't yet got around to playing with tile maps, so I can't say for sure.

  • Posts: 391

    @toadkick, currently, the whole map is created once and then translated into position as the player moves. I think I can imagine how your suggestion can be accomplished. Would making a reference table with the mesh indexes (each tile technically has 4 textures for the autotile process) stored in it be the best way to go about it? Then I could call vertex and texCoord functions to get the needed information of each index.

  • edited August 2013 Posts: 580

    @Slashin8r: Sorry, I've been pondering on this a little more and the solution I proposed is tricky; I need to think over it a bit more. I'll actually be attempting an experiment with this in a couple of weeks, so I'll keep you updated as to how it goes.

    If you are only building the map once, and are seeing framerate issues, then you are probably GPU limited (from the sheer amount of work the GPU is doing), and not CPU limited (which you might be if you were updating mesh verts each frame). I thought in your case it might be the latter, due to re-creating the mesh frequently, but since you're only building the map once that's probably not the case. At any rate, a solution like the one I proposed could potentially help on both fronts, since a) you are only submitting what's actually visible at the time to the renderer (which should reduce strain on the GPU), and b) you are only updating the mesh when it actually needs to be updated, and even then, you only need to update portions of the mesh (which should reduce CPU usage).

    But basically, yeah...in order to determine which rects of the mesh have scrolled off screen, you'd need some way to index them, so that you can move them and assign them different texture coordinates. You'd probably need a table somewhere that maps the mesh rect indices to their location in the map grid, so that when a row/column of the map grid scrolls offscreen, you can round up the mesh rect indices in the row/column, and set their new x/y positions and tex coordinates.

    I actually got this idea from old Nintendo 2D consoles; this is pretty much how it worked, but in hardware. Sorry again for not having brought more ammo to back this idea up at the moment. I'll mull over it a little bit and see if I can't flesh out some more details as to how it might be implemented.

    EDIT: I just realized that I think I'm saying the same thing as @tomxp411.

  • Posts: 157

    @Slashin8r, how many layers are you drawing, and how big are your tiles? I'm just thinking about doing some performance tests...

  • edited August 2013 Posts: 391

    @toadkick, my whole project is based off the old Nintendo 2D classic RPGs, so if it is how Nintendo accomplished it then I'm sure it would fit in perfectly for my project. I'll wait and see what you come up with. Feel free to download and modify the RPGenerator project for testing. I tried to keep most of it commented and user-friendly. I should have my current project out as the latest update by this weekend.

    I have also merged most of my tilesheets (still got decor objects to do) so I have the project down to using only 5 meshes per map. This gave me a boost of about 4 fps on the large map. Seems like I get almost 1 fps per mesh removed. The large map is getting closer and closer to running at my goal of 50 fps on average.

  • Posts: 157

    Here's another idea:

    Render your background on 512x512 chunks. During the draw() function, you only render 4 chunks, in addition to dynamic elements (monsters, doors, etc.)

    As your character moves around the map, you pre-render new chunks just outside of the character's field of view and discard chunks that are no longer necessary.

    There'll be some small lag while chunks are generated, but if you throttle the process so that it only generates one chunk at a time, you can keep the frame rate up and still make the world as massive as you want.

  • Posts: 175

    @Slashin8r whoops good point on the clip function :) I here's another idea, not sure if it will work, how about render your background mesh(s) once to an image and then use another mesh which has a single rect the size of the screen and then set the background image to the texture. Then to scroll with the player offset the position of your background texture with setRectTex. Though of course with this approach there will be some tiling and other management needed for backgrounds larger than the maximum image size and this only in theory works for non-dynamic elements.

  • edited August 2013 Posts: 391

    @toadkick and @tomxp411, I think loading in chunks would be a great way to resolve this. I will definitely look further into this. I really like this idea since it will guarantee that even large maps can be viewed cleanly and read through easily on a small screen such as an iPad. The 2D grid table for the large map I'm using for testing is impossible to read on the iPad. Split that map into chunks of let's say 40x40 tiles would make it easy to decipher. The most tiles that would be rendered at once would be 80x80, or 4 chunks, (the current large map I'm testing with is 130x80) which would cut out 4000 tiles which equals 16000 mesh rectangles (not including certain tiles that share the same location). That would definitely boost performance substantially.

    @tomxp411, there are 5 map layers, 4 of which are being drawn. Floor tiles, animated tiles, object tiles, and decor tiles are all drawn. Then there is the "invisible" event layer.

    Edit: On screen 33 tiles (32x32 in size) are displayed horizontally and I need a buffer of at least 1 on each side, so 35 tiles are needed. 25 tiles are displayed vertically and I need a buffer of 1 on top and 4 below (roof textures are 4 tiles tall), so 30 tiles are needed. So, I think I can make chunks to be 35x30 and everything will continue to look seamless (35x30 maps are generated very fast and at most 2 chunks will need to be generated at any given time).

  • Posts: 391

    Thought a bit more about the chunk idea and I think it might actually be best to generate all the chunk meshes of a map at once, but only draw up to 4 of them at a time. This way there only be one loading time when you first enter a new map and then no loading time for switching out chunks.

    This is definitely going into my next update. Thanks for the brainstorm session everyone.

  • Posts: 157

    We're here to help, man. Well, that and steal your ideas for our own programs... ;-)

    Actually, I've been working on an RPG engine, off and on, for several years, but I've scrapped it and started over. A big reason is that technology has completely changed, and with Windows 8, XBox One, and all the mobile devices out there, I've decided to start over.

    One thing I've definitely figured out: building a good, flexible RPG engine is hard work. =)

Sign In or Register to comment.