Results 1 to 4 of 4

Thread: The upside down problem

  1. #1
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default The upside down problem

    We have a little problem with coordinate systems in ClanLib-0.9. One of the services CL_GraphicContext provides is to maintain a 2D coordinate system. This is handled by the following functions:

    Code:
    set_map_mode(CL_MapMode mode)
    set_viewport(const CL_Rectf &viewport);
    set_projection(const CL_Mat4f &matrix);
    set_modelview(const CL_Mat4f &matrix);
    set_cliprect(const CL_Rect &rect);
    Normally when working with 2D it is natural to use the upper left corner of the window as (0,0) and let the Y axis go downwards. ClanLib therefore per default sets its mapping mode to upper left, and per default lets the viewport span the entire window.

    But when working with 3D it is natural to use the lower left corner of the window as the origin. Since OpenGL was built with 3D in mind, the native origin is lower left with the Y axis going upwards. Furthermore, since textures are normally used for 3D it is a general convention here to let the (0,0) origin of a texture be the lower left part of the image with the Y axis going upwards.

    The problem with using the lower left corner as origin for 2D is that when you resize the window all your objects move on the screen as well. This means applications that do not simply scale the graphics (as 2D apps usually do not) need to add extra logic moving the objects based on the height of the window.

    The upper-left mapping mode of ClanLib serves the purpose of saving applications from having to do this, but the way ClanLib does this today introduces a problem with the images stored in textures. Basically the mapping itself is perfectly okay, but when you work with a Y axis going downwards, you start to find it natural that (0,0) in a texture is also the upper left corner - just like you find it natural to be the lower left corner in 3D where your Y axis goes upwards. So today the image providers in ClanLib load images with (0,0) being upper left and the rows going downwards.

    This approach breaks in two situations:

    1. If you switch the mapping mode to lower-left, or if you draw 3D, you will find the textures to be upside-down
    2. If you render to texture, and then render the texture in the upper-left mapping mode, your texture will appear to be upside down


    First situation breaks because the mapping mode only changes the destination coordinates. So the destination rectangle (i.e. if you use CL_Draw::texture) will have an upwards going Y axis, while the texture image is stored with a downwards Y axis.

    The second situation is slightly more complex to describe. The OpenGL frame buffer always operates with a (0,0) in lower left corner with Y going up. When we bind a texture as the frame buffer, (0,0) in the texture ends up as the lower left corner and what we see visually on the screen going upwards will go downwards in the texture. When CL_GraphicContext applies the upper left mapping mode, what we draw at (0,0) in the upper left corner actually ends up at (0,height-1) in the texture. Since we are used to the image providers making the texture image contain an Y axis going downwards, we will attempt to draw the texture 'as usual' and ends up with an image upside down.

    The render to texture problem was 'solved' by adding the follow hack:

    Code:
    void CL_OpenGLGraphicContextProvider::set_map_mode(CL_MapMode mode)
    {
    	// Invert the mapping mode for FBO's 
    	if (framebuffer_bound)
    	{
    		if (mode == cl_map_2d_upper_left)
    			mode = cl_map_2d_lower_left;
    		else if (mode == cl_map_2d_lower_left)
    			mode = cl_map_2d_upper_left;
    	}
    	...
    }
    At first this solution didn't seem so bad, but I've reached the conclusion it does more damage than good. By inverting the mapping mode, ClanLib causes inconsistency between the managed mapping modes and the cl_user_projection mapping mode.

    This inconsistency becomes really problematic when you use the user projection mapping mode to draw some 3D and then use the 2D mapping mode to overlay it with some 2D. In this situation the application now actually have to attempt to reverse the effects of the invert, or attempt to render the 3D upside down. The end effect is that you cannot anymore simply switch existing rendering code to render to texture and expect it to continue to work (see the current DiceWar GameView for how disastrous it ends up if you do).

    Removing this code still leaves us with two problems though:

    1. We still cannot load an image with the Y axis going upwards in the texture (for those using the 2D lower left mapping mode or doing 3D)
    2. A texture used as a frame buffer will be required to have an upwards Y axis, making all 2D drawing code that expect it to have a downwards Y axis to render it upside down. And for the same reason, we cannot bind a texture we loaded with the image providers and continue painting on it, since it will have been uploaded with a downwards Y axis


    Any suggestions to how we lift these two limitations?

  2. #2
    ClanLib Developer
    Join Date
    May 2007
    Posts
    1,824

    Default

    I can't think of a solution at the moment.

    The problem i have, is with this function, where map_mode != cl_user_projection
    I have to manually call clViewport to set the viewport.
    Code:
    void CL_OpenGLGraphicContextProvider::set_viewport(const CL_Rectf &viewport)
    {
    	if (map_mode == cl_user_projection)
    	{
    		int height = get_height();
    		CL_OpenGL::set_active(this);
    		clViewport(
    			CLsizei(viewport.left),
    			CLsizei(height - viewport.bottom),
    			CLsizei(viewport.right - viewport.left),
    			CLsizei(viewport.bottom - viewport.top));
    	}
    }

  3. #3
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default

    The viewport is also affected by the upside-down problem - at least partially. The viewport in OpenGL is using an origin in lower left corner, while clanlib uses one in the upper left.

    Removing the map mode check from set_viewport is trivial enough and should work, but the question is if it should still use upper left as the origin if the viewport is specified in the lower-left or user mapping modes.

    I think it would be most logical if the viewport is specified with a downwards Y axis in the upper left mapping mode, and an upwards Y axis in the two other mapping modes.

  4. #4

    Lightbulb set_viewport in 2.2.4

    I recently upgraded from ClanLib 2.1.1 to 2.2.4 and spent no fewer than six hours trying to troubleshoot why perspective-mode views no longer worked properly at all.

    Note that my viewport is not the full screen; rather, I have multiple, smaller viewports scattered throughout a ClanLib GUI; each of my viewports is a CL_GUIComponent. Thus, each component can only render on a limited portion of the screen.

    It looks like calling "set_viewport" in 2.1.1 used the top-left coordinate system, while in 2.2.4 it is certain that it uses the bottom-left coordinate system. This seems to be a silly thing to do in that, from a GUIComponent's point of view, the height of the full window is not known (nor does it need to be).

    This was fine for an top-left setup; "component_to_window_coords" easily took care of mapping a GraphicContext's coordinates to those of the full window, which is what is necessary for any calls to "set_viewport".

    My solution is in regards to "CL_GUIComponent:: on_render":
    Code:
    ...::on_render( CL_GraphicContext &gc, const CL_Rect &clip_rect ) {
    ...
    To address this in 2.2.4, I had to first get the window's geometry by calling:
    Code:
    CL_Rect windowGeometry = get_display_window().get_geometry();
    Then, I had to translate my desired viewport to bottom-left coordinates by doing:
    Code:
    CL_Point newTopLeft = component_to_window_coords( CL_Point( clip_rect.left, clip_rect.top ) );
    int width = get_width();
    int height = get_height();
    CL_Rect newClipRect = CL_Rect( newTopLeft, CL_Size(width,height) );
    CL_Rect backwardRect( CL_Point(newTopLeft.x,windowGeometry.get_size().height-testTopLeft.y-height), CL_Size(width,height) );
    ...
    gc.push_modelview();
    gc.set_map_mode(cl_user_projection);
    gc.push_cliprect();
    gc.set_cliprect( backwardClipRect );
    gc.set_viewport( newClipRect );
    
    CL_Mat4f perp = CL_Mat4f::perspective( 45.0f, ((float)width) / ((float)height), 0.1f, 10000000.0f );
    gc.set_projection( perp );
    Calling "gc.set_map_mode(cl_map_2d_upper_left);" did nothing to get "set_viewport" to understand that I wanted top-left coordinates, no matter where I put it.

    Also note that after doing any work with a CL_GUIComponent's "on_render" function, you should set the map mode to cl_map_2d_upper_left; otherwise, it's not too happy.

Similar Threads

  1. Uhhh upside down funny??/
    By darknet12282 in forum Novashell Game Creation System
    Replies: 2
    Last Post: 05-24-2012, 01:50 AM

Bookmarks

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •