Sign in to follow this  
Icebone1000

Dynamic objects in Isometric Map Drawing algorythms

Recommended Posts

Icebone1000    1958
I just finished my Isometric drawing class that can draw 2D cubic tilemaps (its not a single plane with objects on top, its a lot of cubes piled, planes divided on what I call floors (no, Im not cloning minecraft)).

I can draw in diamond shape and in the staggered shape, as described in most algorithms out there..Whats holding me up now is something I didnt find in any isometric article/topic out there, how do I draw characters/objects on it in a optimal way?

What Im doing now, as one can imagine, is traversing my grid/map ( its a 3d array) and drawing the tiles in a order so alpha blending works correctly.

Well, things are a lot easier when you just have a plane with objects on top of it, but since my grid is 3D, I cant see any other way than having to draw all my objects at the same time Im drawing the map, with screws the cuteness of the code..

I cant imagine a good way to do this, things that I can imagine is:

#1# give a list of objects(sprites and its positions) to the iso drawer, each loop iteration on the map drawing algo checks the position of all objects against the current tile on the grid....This is the most inefficient thing I ever imagined (for each tile on the map, check all the objects..)

#2# each time a object moves, insert it ON THE GRID, this is so ugly..screws everything, the isodrawer now will change a lot since now its not more a map drawer, its the entire game drawer (except the HUD)(well, this is true for #1# too)...Second problem with this approach is that objects can move freely, not per tile movement, so an object can occupy 2 tiles (be between 2 tiles), dont know how Id handle it.

#3# Forget the " iso drawer "map drawing algo" ", transform it onto a " "depth list" composer ", the list consist on [2D position, uv, Z(depth)] of all tiles on the map, then insert ..somehow..the objects on that list, then draw the list...I cant say for sure if this is feasible, youd have all tiles Z values, than you can compute all objects Z values, now totally independent, (the trick here would compute the Z of the objects considering its position on the map (i.e. object at floor 2, row 4, col 1 : Z = ? ) and the tiles Z too(giving top floors tiles higher Zs, etc.).......

Im using XNA spritebatch for doing this, I think (not sure, I just started using xna) that it can draw sorted by Z order for you, so I wouldnt need to sort the list.

Any links, tips or ideas are welcomed ;D

"why dont you do it 3D?" 3D is good for ultra realistic graphics, for simpler stuff it looks terrible, because 2D art is owsome and cant be compared with 3D, they are two different fields, not the evolution of the other, because I prefer it, because Im fan of isometric 2D for long, because programming iso stuff is tricky and is forcing my mind a lot.....IMO


Heres a screen shot of what I current have:
[img]https://lh5.googleusercontent.com/-pIMgk74fQGg/UCevCkXHGNI/AAAAAAAAAVg/Q793EL1sRH8/s800/isoSS_cubicTilemap.png[/img] Edited by Icebone1000

Share this post


Link to post
Share on other sites
JTippetts    12970
I fear you misunderstand exactly what "3D" is. 3D doesn't have to look terrible, good, ultra-realistic, or anything else. 3D is merely a set of mathematical transformations. An abstraction, if you will. You can accomplish in 3D [i]exactly[/i] the visual appearance you have already established with your 2D, by using an orthographic transformation and your 2D graphics view-projected onto properly-shaped primitives. But it will have the added bonus of a Z-buffer which will greatly aid your efforts of populating the map with dynamic objects. It will certainly be at least an order of magnitude easier than hacking together some kind of god-awful convoluted draw algorithm, as you seem to be working on. On a platform with the proper hardware support, I can think of absolutely zero valid reasons to be doing something like this the "old school" way. This is coming from someone who has written many isometric games and prototypes, who loves the isometric look and feel more than any other game.

It's all about utilizing the tools that modern technology provides to overcome some of the hurdles that people have been struggling with for years. Feel free to search the extensive back-history of the old Isometric Games forum here at gamedev.net. You will find countless posts on this very issue. What you won't find is a solution that is simple and elegant.... unless you go 3D.

Share this post


Link to post
Share on other sites
Icebone1000    1958
Well, by 3D I really meant 3D cubes, but if you talking about "billboards", 3d quads with a texture, than we are talking about the same thing..I dont see how it changes anything..??
I plan to migrate all that stuff to my sprite engine that Im working on on dx11, with is exactly a ortho projection and quads, see?..With I believe must be the same thing xna sprites are...

Would you enlight me on how youd do it?

Share this post


Link to post
Share on other sites
JTippetts    12970
I'm not talking about billboards, except maybe for the mobs. The world geometry should be cubes (or, rather, your shortened cubes). That way, the depth buffer will have the depth that it needs to clip your mobs after the world is drawn. Just take your 2D landscape sprites and project them onto shortened cubes, ramps, etc... Since the view never changes, your cube primitives only need to consist of the three visible faces.

Share this post


Link to post
Share on other sites
Icebone1000    1958
Translating pixel art to textures so you can map to ortho projected cubes is that easy? and will look the same?
The steps to do it, as Im guessing:
Use nearest/point filtering when strecthing the original 2d image (on photoshop i.e.) to produce the texture map, and then sample it with point sampling onto a ortho projected cube..
I just cant believe it will look exactly like the original pixel art o_o is that what you saying?

Because if you just saying it will look good and 2D, its still not enough reason to consider it, the great deal with 2d iso games, is the 2d iso art..

Even if it do will look just like..

One problem already is to make that translation, you have to do it manually for all tiles, or you build a tool to do it for you, with will have to account for all possible shapes you have in your tilseset.

Forgetting the tiles and do the textures straight ahead is also a complete different matter, I mean, how would you draw a grass texture for a cube, so it can look a nice 2D grass after projected, and not just a texture on a 3D cube? Youd have to draw the texture accounting for how much it will be stretched on Y, not feasible either..Youd need a different artist, not a good 2D pixel artist.

Then that tool to make the conversion automatically seems the better solution, but building it seems as tricky as all the isometric old school stuff..

I dont think theres a direct translation from 2D to 3D as you make it look, unless Im dont get what you mean yet, I remain with I said in the first post: 2D and 3D are two completely different art fields..

Share this post


Link to post
Share on other sites
Icebone1000    1958
Thats freaking nice..Im not trying to forcing myself onto old school stuff either, its just that accepting you can do the same on 3D never seemed possible to me.

When mapping the tiles to the cubes on blender, you didnt even need to stretch the tile/texture? you can just map directly?

One thing that holded me up on that is the tile spacing, when dealing with pixel art, you account the amount of pixels from one tile to another, and how much pixels you overlap(or not), with also means you can have different shaped iso tiles (mine have 2 horizontal px on the top and bottom corners, 2 px horizontal on the left right ones, on the top and bottom you must overlap by one pixel, on the left and right you overlap by too, I already saw many tilesets with different proportions but same perspective(like no overlapping at all, 3 px at top and bottom, etc.)) I never imagined this fitting on 3D stuff without losing the 2D appeal.

Can I assume irregular tiles like fancy curved slopes, like on this famous tileset:
[img]http://www.pixeljoint.com/files/icons/full/iso_64x64_outside_both.png[/img]
Will also work fine? o_o look all the irregular, not straight cube stuff on these tiles.

Share this post


Link to post
Share on other sites
JTippetts    12970
Yeah, you can map all of those to cubes. It really is just like mapping them to billboards, only you use cubes instead so they have depth. Mapping it to cubes will give it the shape of a cube, though, so in the case of some of those ramps and such you can deform the cube somewhat, maybe even subdivide it one or two times, to give it a shape that more closely approximates the sprite it is meant to represent. The key word is approximate; you don't need to get crazy.

As far as mapping the sprite onto the geometry, there is no stretching or tiling. Here is how I do it in Goblinson Crusoe and all my other ventures.

1) Start with a cube in Blender.

2) Since only three faces (top, front-left, front-right) are visible, I delete the non-visible faces.

3) Set up an orthographic camera that is rotated 30 degrees on the X axis, 45 degrees on Z. This gives a viewpoint that mathematically implements the 2:1 tile ratio size of most "standard" isometric games. Hit Numpad-0 to go to camera view so I'm looking at the cube from the perspective it will be viewed in game.

4) Tab into Edit mode on the cube, press 'u' for UV unwrap, and select Project to View (bounds). What this does is assigns UV coordinates based on the current camera view. In effect, it flattens the cube as it is being viewed into a UV map. The result is something like this:

[img]http://i.imgur.com/Jvggp.png[/img]

4) Export the cube geometry (or write down the verts and UVs and just enter them manually into a geometry array in your program, if you don't want to much with model loading.

5) Draw the cube with the tile set as the texture, with Nearest filtering.

You can see that the UVs are projected exactly as the cube is viewed. The only slightly tricky part here is using trigonometry to calculate how much to scale the unit-sized cube vertically to fit the desired tile size. This is something you can either calculate mathematically or by trial and error. You want to scale it so that the UVs line up with your tile exactly as so. Then you want to scale the cube itself, which will be drawn by your engine, so that it will fit the tile size in the orthographic projection.

You do have to think about the 3D math that underlies the abstraction, but as you gain a deeper understanding of 3D it becomes no problem. By setting up your projection carefully, you can draw the cube geometry such that it will cover exactly the same screen space as the 2D version, pixel for pixel.

There is one small caveat: you do want to avoid using too much floating point in your abstraction. I typically set my orthographic projection up on a 1:1 basis; that is, 1 pixel in screen space equals one unit in 3D space, and always clamp translations to integers. The reason for this is that floating point math can get a little bit "leaky", and you might sometimes get rasterization artifacts when you are trying to get pixel-perfect rendering of your sprite. You can also pad your sprites, duplicating pixels all the way around the shape, so that if the occasional off by one funky raster does take place, it won't be noticeable. However, if you are careful with your math, that shouldn't really be a problem.

Maybe if I get a few minutes at work, I'll pop together a demo.

Share this post


Link to post
Share on other sites
Icebone1000    1958
Im having artifacts on XNA already when zooming (scaling) the camera/view matrix, not a 3D exclusive issue ;D
My WIP sprite engine on Dx11 is pixel perfect already, but I have a scale unscale thing going on so I can work with physics with a px not being a metter, so basically I have a to_Metter on sprite sizes, and a to_PX on my projection matrix (the inverse of the to_Metter).. I probably can just plug cubes instead of quads and it will work, and workout the camera position.

But never done anything in 3D with XNA yet (started messing with XNA last week)

How do you turn off all filtering on blender? I turned mipmaps off on the system tab, but it stills have some filtering going on (left model, right texture):
[img]https://lh3.googleusercontent.com/-1qlmw5NEp2A/UCkPBbjo0EI/AAAAAAAAAV0/dxxQXQ9E4tk/s800/isoblenderss.png[/img]

From programming to modeling questions ;D

"Maybe if I get a few minutes at work, I'll pop together a demo."
._. * humiliation * Edited by Icebone1000

Share this post


Link to post
Share on other sites
Bluefirehawk    1232
Thank you very much JTippetts, this example is awesome.
I want to make a similar isometric engine, but I am not on the same level as you guys. Can you clarify stuff a bit for me?

[b]Tile and funky value[/b]
Looking at the tiles you used, it looks like the tile primitive is 0.5 of the width. And the funky value is 0.5 in perspective of 30° angle. Am I on the right path here?
I tried to reproduce your calculation, but I haven't managed to do so yet, can you describe the formula?

[b]World Unit[/b]
As I understood, your world is rasterized in world units, one tile consists of 16*16 units. You wrote that an actor traversing the tile needs 16 steps. So a mob in your engine would keep track of the mobs unit he is currently in, right?

[b]Zooming[/b]
If you would implement zooming, it looks like you would need to reinit OpenGL to a different resolution.This seems a bit cumbersome, is there an other solution?

Share this post


Link to post
Share on other sites
JTippetts    12970
1) The "funky value" for the foreshortening of the unit cube was calculated as [b](0.5/cos(30)) / (sqrt(2))[/b]. I don't really have time to do a diagram, but maybe a description will do. The vertical walls in the tile sprite are 0.5 times as tall on-screen as the top of the tile. The sprite is being viewed at an angle of 30 degrees. Doing a little trig, you can see that if the tile top is considered 1 unit in size on-screen (it's not, but for the purposes of the math it is) then the foreshortening factor to use to make a unit-size cube half that size in screen space would be [b]0.5 / cos(30)[/b]. Now, since the tile top is actually being viewed diagonally, it's size is actually [b]sqrt(2)[/b], so we need to divide [b](0.5 / cos(30)) / sqrt(2)[/b] to get the final foreshortening factor. Scale a tile-sized cube by this factor vertically, and it should match the sprite tile pattern.

(This kind of weird math is the most difficult part of tranforming 2D to 3D isometric. You just need to get a sheet of paper and draw some diagrams of exactly what is happening, as far as the interaction of a camera at 30 degrees / 45 degrees is concerned. Once you wrap your head around it, the rest is easy peasy.)

2) Not necessarily. I just have my mobs keep track of their [b]world coordinate[/b]. There are 16 world units per tile. So a tile world 10x10 tiles in size in X and Z would be 160x160 units in size. To find the tile any given mob is in, you just divide his world coordinate by 16. Now, I usually do use a spatial grid structure to track mobs, but that is typically for visibility determination.

3) No, all you have to do is scale the [b]ortho_width[/b] and [b]ortho_height[/b] values when constructing your projection matrix by 1/zoomfactor. So if you want a zoom of 2, just scale by 0.5. To make it easy, you could just alter the [b]applyCamera[/b] function:

[code]
void CIso::applyCamera(float zoomfactor)
{
GLint viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float aspect=(float)viewport[3]/(float)viewport[2];
float screen_tiles=(float)viewport[2] / (float)tile_pixel_width_;
float ortho_width=screen_tiles*(float)tile_world_size_*1.414213562373f * (1.0f/zoomfactor); // Scale the horizontal by zoomfactor
float ortho_height=ortho_width * aspect;
glOrtho(-ortho_width/2,ortho_width/2,-ortho_height/2,ortho_height/2,0.0f, 1000.0f);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0,0,-100);
glRotatef(30,1,0,0);
glRotatef(-45.0f,0,1,0);
glTranslatef(-x_,0, -z_);
}
[/code]

That'll let you zoom in or out to whatever level you desire, any time you desire. No resizing the GL viewport is necessary. Edited by JTippetts

Share this post


Link to post
Share on other sites
Icebone1000    1958
You still will need to sort the alpha blended sprites right? I mean, sprites behind a tile will need to be clipped(partially) by the front tile, but the blending need to occur to the tiles behind the sprites..Isnt true?
Doesnt that mean that the entire map will need to be sorted anyway? At least if you want a "pilled cubes" kind of isometric game.

One problem Im trying to figure out (in 2D) is when the height of a object is greater then an entire floor, I mean, if you draw the first floor (the one with a 2 floor height object on it) and than you draw the second floor, the second floor objects will clip the tall object even if behind it. I though I have solved it by drawing all the floors of each tile ( instead of traversing floor/row/col, Im traversing row/col/floor, check the image on the op, theres 2 floor tall objects), this really solved for the map itself (static) but for moving objects it does not work, since the object will be clipped by tiles on front rows(on the same floor), its "feet" will be cliped by the "losang" right bellow it, on the front row, before it jumps to the next row (you know, when its transiting between tiles). One may think a solution is to not have objects taller than a floor, but them..your character can not jump? inadmissible!

Neither I can understand what I described, heres a pic:
[img]https://lh3.googleusercontent.com/-nzOxYZ587Ps/UC-ocO3nv6I/AAAAAAAAAWY/Nhs6z2w5jBQ/s436/isocubicfloorissue2.png[/img]
The tall cube is being cliped by the tile on the floor above, cause the top floors are drawed later.
[img]https://lh5.googleusercontent.com/-uMWeTJk3Y2k/UC-bK5V6h8I/AAAAAAAAAWI/V0Je1qcxoBY/s436/isomovingfloorissue.png[/img]
Here I solve the first problem, by drawing all floors of a tile first..the object is on the second floor, but its clipped by the one on the floor bellow, cause the front rows are drawed later now..

Resuming, the problem is solving transitions, if you solve transitions on width, you ruin on height, if you solve on height (y), you ruin on width.

I cant visualize if this problem will be solved on 3D, you still need alpha blending and sorting right?

How engines solve problem like this (kinda off topic):
[img]https://lh6.googleusercontent.com/-FXBSptFuADI/UC-tKoBwBAI/AAAAAAAAAWo/PNd1QSXN8NI/s800/alphablendcullingissue.png[/img]
Lets say the sprite is a smoke particle, how you do the blending of the smoke with the heightfield behind it, without having the smoke showing on front of the height field part on front of it?

Share this post


Link to post
Share on other sites
JTippetts    12970
You only need to sort if you actually do alpha blending, with partial transparency. If you just do alpha testing, where a pixel is either fully opaque or fully transparent, you do not need to sort. Typically, I do not see pixel art Iso games use partial transparency. The above demo uses full transparency and draws front to back to take advantage of early depth rejection to reduce overdrawn.

For sprites like smoke that must use partial transparency you can sort those sprites alone and draw them in a pass after the world is drawn. Depth testing will reject any pixels that do not pass the depth test. Edited by JTippetts

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this