Jump to content

  • Log In with Google      Sign In   
  • Create Account


2D Tile Based World Problem


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
18 replies to this topic

#1 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 02 September 2012 - 09:34 PM

I have a 2D world built of tiles, mapped to their proper coordinates in the world. I've set my viewport and GlOrtho to the size of the screen, and am using glScale to zoom in/out. Only when I do it I'm only seeing the part of the world originally rendered. I believe this to be because my glOrtho is restricting the tiles that are drawn. So do I need to reset my viewport, then my glOrtho and then do my glScale?

Sponsor:

#2 dpadam450   Members   -  Reputation: 842

Like
0Likes
Like

Posted 02 September 2012 - 09:47 PM

If you take a digital camera and hit zoom, do trees and people get bigger? No, the camera just sees less of the world. To scale with ortho you need to change how much it sees. Only the viewport is the size of the screen. Ortho is how far left in the world will hit the left edge of your screen and how far right in the world will map to the right.

#3 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 02 September 2012 - 10:44 PM

So I leave the viewport set to the resolution of the screen, and use glOrtho to control the left and right mappings of the world. glScale just makes things bigger or smaller, so probably is not the best way to zoom in and out since I'll either be rendering more than I need to, or have to change my ortho because I'm not rendering enough. True?

#4 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 03 September 2012 - 12:41 AM

That did it. The viewport is set to the screen pixel resolution, and then ortho is being used to set what corresponds to the left and right portions of the screen. Now to just get the zoom working. I see two choices... Continue using glScale, which seems easiest, or further adjust the ortho to also reflect the zoom, which is slightly more complex because I have to keep the aspect ratio in mind. I'm off to experiment a bit. Thanks! =D

#5 Goran Milovanovic   Members   -  Reputation: 1103

Like
0Likes
Like

Posted 03 September 2012 - 01:46 AM

For orthographic zooming, I typically just scale the modelview matrix:
[source lang="cpp"]glMatrixMode( GL_MODELVIEW );glScale(1.1f, 1.1f, 1.0f);[/source]
There are other methods, like scaling the projection matrix, but they usually require additional translations to do a proper "in place" zoom, whereas this method "just works" (in my experience).

+---------------------------------------------------------------------+

| Need a programmer?        ->   http://www.nilunder.com/protoblend   |

| Want to become one?       ->   http://www.nilunder.com/tutoring     |
| Game Dev video tutorials  ->   http://www.youtube.com/goranmilovano |
+---------------------------------------------------------------------+

#6 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 03 September 2012 - 10:57 AM

Alright... so what I need to do is to make the default rendering view be a fully zoomed out view, and then use glScale to zoom in. My problem at the moment is that the zoom level that I render at is somewhere in the middle of the zoom levels I want to make available. I'll rework it a bit and it should work great. I wonder if there's a way I could also move the clipping plane when I zoom so the unseen geometry isn't being rendered as well....

#7 Goran Milovanovic   Members   -  Reputation: 1103

Like
1Likes
Like

Posted 03 September 2012 - 01:42 PM

I wonder if there's a way I could also move the clipping plane when I zoom so the unseen geometry isn't being rendered as well....


...?

By definition, "unseen" geometry is not rendered.

+---------------------------------------------------------------------+

| Need a programmer?        ->   http://www.nilunder.com/protoblend   |

| Want to become one?       ->   http://www.nilunder.com/tutoring     |
| Game Dev video tutorials  ->   http://www.youtube.com/goranmilovano |
+---------------------------------------------------------------------+

#8 Trienco   Crossbones+   -  Reputation: 2056

Like
0Likes
Like

Posted 03 September 2012 - 10:47 PM

If you want to zoom, zoom. Don't scale, don't move closer to the object or any of the other misguided attempts to make stuff "look bigger" that will eventually result in clipping if you move to close or scale too much.

Imagine you zoom in on a house, but a branch from a tree in front of you is covering half of it. Does it make any sense for a zoom to magically have the branch disappear, because it got clipped or you moved past it?

As dpadam450 already said, zoom by adjusting the projection matrix like a "real camera" and you won't get any unexpected effects.

Why would you care about the aspect ratio? It doesn't change if you multiply ALL sides by the same factor (as in: the two dimensions that matter).

Get in the habit of setting your ortho projection at the beginning of each frame, multiply your dimensions with your current zoom factor and be done with it. It works best if your projection is symmetric (from -x to +x instead of from 0 to +x) or you will need to also shift the values to avoid drifting closer to one corner.

Zoom in: zoom factor / 1.2, zoom out: zoom factor * 1.2. (or whatever numbers works for you)

Edited by Trienco, 03 September 2012 - 10:52 PM.

f@dzhttp://festini.device-zero.de

#9 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 04 September 2012 - 12:56 AM


I wonder if there's a way I could also move the clipping plane when I zoom so the unseen geometry isn't being rendered as well....


...?

By definition, "unseen" geometry is not rendered.


If you render a triangle and then use glScale to zoom as is suggested by some, OpenGL is still rendering the triangle, even though you can't see it. Or so I'm told.

#10 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 04 September 2012 - 01:25 AM

If you want to zoom, zoom. Don't scale, don't move closer to the object or any of the other misguided attempts to make stuff "look bigger" that will eventually result in clipping if you move to close or scale too much.

Imagine you zoom in on a house, but a branch from a tree in front of you is covering half of it. Does it make any sense for a zoom to magically have the branch disappear, because it got clipped or you moved past it?

As dpadam450 already said, zoom by adjusting the projection matrix like a "real camera" and you won't get any unexpected effects.

Why would you care about the aspect ratio? It doesn't change if you multiply ALL sides by the same factor (as in: the two dimensions that matter).

Get in the habit of setting your ortho projection at the beginning of each frame, multiply your dimensions with your current zoom factor and be done with it. It works best if your projection is symmetric (from -x to +x instead of from 0 to +x) or you will need to also shift the values to avoid drifting closer to one corner.

Zoom in: zoom factor / 1.2, zoom out: zoom factor * 1.2. (or whatever numbers works for you)


I will do this and report back.

#11 Goran Milovanovic   Members   -  Reputation: 1103

Like
0Likes
Like

Posted 04 September 2012 - 05:55 AM

If you render a triangle and then use glScale to zoom as is suggested by some, OpenGL is still rendering the triangle, even though you can't see it. Or so I'm told.


As far as I know, everything that you can't see on screen is implicitly outside of Clip Space, and therefore not rendered ... But maybe there's something that I don't know about.

In either case, here's a complete zoom example written in C, using SDL for basic OpenGL context and input handling:

[source lang="cpp"]/** zoomdemo.c** Zooming example by Goran Milovanovic, for Lance42.** Arrow keys move the little square around.* W key zooms in; S key zooms out.* ESC to quit (or just close the window).** Compile command used: gcc -Wall -o zoomdemo -lGL -lSDL zoomdemo.c**/#include "SDL/SDL.h"#include "GL/gl.h"SDL_Surface* createDisplay(short width, short height){ SDL_Init(SDL_INIT_EVERYTHING); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_Surface* screen = SDL_SetVideoMode(width, height, 32, SDL_OPENGL); return screen;}void processEvents(char* keyboard){ static SDL_Event event; while ( SDL_PollEvent(&event) ){ switch (event.type){ case SDL_KEYDOWN: keyboard[ event.key.keysym.sym ] = 1; break; case SDL_KEYUP: keyboard[ event.key.keysym.sym ] = 0; break; case SDL_QUIT: exit(0); } }}void glInit(SDL_Surface* screen){ // Set clear color (black) glClearColor( 0, 0, 0, 1 ); // Set projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); float aspect_ratio = (float)screen->w / screen->h; float X = 10; float Y = X / aspect_ratio; glOrtho( -X, X, -Y, Y, 1.0, -1.0 ); // Set modelview matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity();}void glZoom(float scale){ glMatrixMode(GL_PROJECTION); glScalef(1 + scale, 1 + scale, 1);}typedef struct Vec2{ float x; float y;} Vec2;typedef struct Square{ Vec2 v_coords[4]; Vec2 pos;} Square;void squareInit(Square* square){ Vec2* v = square->v_coords; v[0].x = -1; v[0].y = 1; // 0-----1 v[1].x = 1; v[1].y = 1; // | | v[2].x = 1; v[2].y = -1; // | | v[3].x = -1; v[3].y = -1; // 3-----2 square->pos.x = 0; square->pos.y = 0;}void squareDraw(Square* square){ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(square->pos.x, square->pos.y, 0); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, square->v_coords); glDrawArrays(GL_QUADS, 0, 4); glDisableClientState(GL_VERTEX_ARRAY);}int main(int argc, char* argv[]){ SDL_Surface* screen = createDisplay(640, 480); glInit(screen); char keyboard[322]; memset(keyboard, 0, sizeof(keyboard)); Square square; squareInit(&square); while (1){ processEvents(keyboard); if (keyboard[SDLK_ESCAPE]) break; glZoom((keyboard[SDLK_w] - keyboard[SDLK_s]) * 0.1f); // Incremental zoom // Move the square square.pos.x += (keyboard[SDLK_RIGHT] - keyboard[SDLK_LEFT]) * 0.25; square.pos.y += (keyboard[SDLK_UP] - keyboard[SDLK_DOWN]) * 0.25; glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer, so we can draw to a clean slate. squareDraw(&square); // Draw whatever you want to draw. SDL_GL_SwapBuffers(); // Update the screen with the newly rendered image. SDL_Delay(16); // Close enough to 60 fps. } return 0;}[/source]

I'm doing it the "right way" as Trienco rightly pointed out: My glOrtho call creates a symmetric projection, and the zooming is done by simply scaling the projection matrix.

If you have any questions, feel free to ask.

+---------------------------------------------------------------------+

| Need a programmer?        ->   http://www.nilunder.com/protoblend   |

| Want to become one?       ->   http://www.nilunder.com/tutoring     |
| Game Dev video tutorials  ->   http://www.youtube.com/goranmilovano |
+---------------------------------------------------------------------+

#12 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 04 September 2012 - 11:32 AM

Thank you for the example. I will definitely use it to check my work and make sure I'm doing it correctly!

Lance...

#13 Trienco   Crossbones+   -  Reputation: 2056

Like
0Likes
Like

Posted 04 September 2012 - 09:53 PM

I'm doing it the "right way" as Trienco rightly pointed out: My glOrtho call creates a symmetric projection, and the zooming is done by simply scaling the projection matrix.


Actually no, because I pointed out that zooming doesn't belong in the modelview matrix at all. Scaling to zoom can make objects close to the camera suddenly disappear if they "grow" beyond the near clipping plane.

If you want to zoom the way an actual camera does it, you simply use this and stay away from the modelview matrix:

glOrtho( -X/scaleFactor, X/scaleFactor, -Y/scaleFactor, Y/scaleFactor, 1.0, -1.0 );

This will make sure that zooming while standing right in front of a wall doesn't turn into a wall hack.


As for rendering invisible geometry. OpenGL will render everything you throw at it, because it can't tell if something is visible without first transforming all vertices and performing clipping. That only saves you half the work (probably more, as rasterizing and shading are usually the expensive parts).

If you don't want something drawn, don't send it to OpenGL. Culling should be pretty simple when dealing with 2D tiles, even with zooming (it's just a factor after all).
f@dzhttp://festini.device-zero.de

#14 Goran Milovanovic   Members   -  Reputation: 1103

Like
0Likes
Like

Posted 04 September 2012 - 10:35 PM

Actually no, because I pointed out that zooming doesn't belong in the modelview matrix at all. Scaling to zoom can make objects close to the camera suddenly disappear if they "grow" beyond the near clipping plane.


Did you read my code?

I'm scaling the projection matrix, not the modelview matrix.

+---------------------------------------------------------------------+

| Need a programmer?        ->   http://www.nilunder.com/protoblend   |

| Want to become one?       ->   http://www.nilunder.com/tutoring     |
| Game Dev video tutorials  ->   http://www.youtube.com/goranmilovano |
+---------------------------------------------------------------------+

#15 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 05 September 2012 - 12:30 AM

As for rendering invisible geometry. OpenGL will render everything you throw at it, because it can't tell if something is visible without first transforming all vertices and performing clipping. That only saves you half the work (probably more, as rasterizing and shading are usually the expensive parts).

If you don't want something drawn, don't send it to OpenGL. Culling should be pretty simple when dealing with 2D tiles, even with zooming (it's just a factor after all).


I was hoping that OpenGL had optimized ways of figuring out what not to bother rendering, but if I have to figure that out myself I can. Thanks. =D

#16 Lance42   Members   -  Reputation: 339

Like
0Likes
Like

Posted 05 September 2012 - 01:31 AM

What I ended up doing which seems to work:

	    public void UpdateCamera(Vector controlInput, double elapsedTime)
	    {
		    double scaleOffset = (_scale - 1) * -96;
		    worldOffset += (controlInput * elapsedTime * 256);
		    Gl.glMatrixMode(Gl.GL_PROJECTION);
		    Gl.glLoadIdentity();
		    Gl.glOrtho(-640 + worldOffset.X + scaleOffset, 640 + worldOffset.X - scaleOffset, -360 + worldOffset.Y + scaleOffset, 360 + worldOffset.Y - scaleOffset, -100, 100);
		    Gl.glMatrixMode(Gl.GL_MODELVIEW);
		    Gl.glLoadIdentity();/* */
	    }

This seems to work well. The worldOffset is a vector containing the an X and Y offset putting the camera over the proper place in the world. The scaleOffset is what does the scaling magic. The screen resolution is hard coded for testing purposes, and the hard coded 96 and 256 numbers come from the texture sizes and player movement speed, respectively. Have I done this completely wonky?

#17 Trienco   Crossbones+   -  Reputation: 2056

Like
0Likes
Like

Posted 05 September 2012 - 10:28 AM

Did you read my code?

I'm scaling the projection matrix, not the modelview matrix.


My bad, I was just quickly scrolling back up and saw your first post, where it was the model view.

Have I done this completely wonky?


Well, I don't think adding a scale offset instead of multiplying with a scale factor makes much sense, especially since I would expect that to screw up your aspect ratio.

Let me dig out some old code..
void zoomBy( float factor )
{
    scroll += ( factor < 1.f ? mousePos : Vector3(worldWindow.w/2, worldWindow.h/2,0) ) * invZoom*(1.f-factor);
    invZoom *= factor;
}

void zoomIn() { zoomBy(1.25f); }
void zoomOut() { zoomBy(.8f); }

//When setting up the frame for rendering...

glViewport(worldWindow.x, worldWindow.y + (screenHeight-worldWindow.h), worldWindow.w, worldWindow.h);

glOrtho(scroll.x, scroll.x + (worldWindow.w*invZoom), scroll.y + (worldWindow.h*invZoom), scroll.y, -1000000, 1000000);

This is probably confusing more than helping. It will always zoom towards the location of the mouse pointer, but when zooming out it uses the center of the view. I find it a lot more convenient than zooming towards the center of the screen and it even lets you quickly zip all over the map without actually having to scroll, just by zooming in and out.

It's also (ab)using the projection to scroll, though one could argue that the "camera position" should be in the modelview, not the projection. The reason is simply so I won't have to consider the current zoom factor when scrolling (you will want to scroll faster when zoomed out and slower when zoomed in).

Note that zooming back out would be easier if the projection was centered around the origin, so I wouldn't have to correct the scroll offset. But since I need a function that will zoom towards/from any arbitrary point anyway, it wouldn't really make much difference and I find it very tedious to think of the screen as going from -width/2 to +width/2 instead of 0 to width.

In this case the "worldWindow" is the area where the world is drawn. I set the viewport to leave out the area at the bottom where the GUI stuff will go. Unless you want to limit the viewport, you can pretend that worldWindow.x/worldWindow.y are 0 and worldWindow.w/worldWindow.h are your screen or window size. So basically looking at my old code it's not even required to have your view centered around the origin.

Edited by Trienco, 05 September 2012 - 10:29 AM.

f@dzhttp://festini.device-zero.de

#18 Goran Milovanovic   Members   -  Reputation: 1103

Like
0Likes
Like

Posted 05 September 2012 - 02:03 PM

@Lance42

For your purposes, where you just want to zoom in and out (as a camera typically would), my example represents a simpler, and arguably more elegant solution.

You should really take the time to understand it. The key thing to note: I'm not making multiple calls to glOrtho. I just call it once to initialize the matrix, and the zoom is simply a scaling operation implemented via glScalef.

Ultimately, it's your decision, but I would prefer the zoom function that is both easier to implement, and easier to understand.

@Trienco

Well, from what I understand, he's not trying to shift the zoom point.

However, if he wanted to do that, I would recommend doing it like this:

// Pseudo code

delta = mouse.worldPosition - camera.position
camera.position = mouse.worldPosition
camera.zoom(scale)
delta *= scale
camera.position -= delta

I think that would be cleaner (no need to "dirty" the projection matrix with modelview data).

+---------------------------------------------------------------------+

| Need a programmer?        ->   http://www.nilunder.com/protoblend   |

| Want to become one?       ->   http://www.nilunder.com/tutoring     |
| Game Dev video tutorials  ->   http://www.youtube.com/goranmilovano |
+---------------------------------------------------------------------+

#19 Trienco   Crossbones+   -  Reputation: 2056

Like
0Likes
Like

Posted 05 September 2012 - 11:22 PM

Well, looking back I can now say that decision was made purely for the sake of simplicity and keeping things intuitive. This way the entire "view setup" is in the ortho projection by simply specifying the chunk of the world you want to see (in convenient world coordinates).

Doing it that way is more straight forward than creating a projection at a fixed location and then "shifting the world into place" and when dealing with numbers, I think the easier you can picture it, the better. Of course it doesn't transfer well to perspective projection, so if there are any plans to switch, it's better put in the modelview from the start.

In fact, the modelview isn't used at all, since for a simple 2D tile engine, immediate mode is more than fast enough, even when zooming out a lot. Trying to group tiles into chunks just to use vertex buffers would have been a waste of time, making things more complicated without any real benefit.
f@dzhttp://festini.device-zero.de




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS