Results 1 to 8 of 8

Thread: Upside down sprites

  1. #1

    Default Upside down sprites

    Here's my projection:

    Code:
      gc.set_map_mode(cl_user_projection);
    
      // params are left right bottom top
      gc.set_projection(CL_Mat4f::ortho_2d(
        m_camera_location.x - m_camera_size.x, 
        m_camera_location.x + m_camera_size.x, 
        m_camera_location.x - m_camera_size.y, 
        m_camera_location.x + m_camera_size.y));
    which comes out to being something like
    Code:
    gc.set_projection(CL_Mat4f::ortho_2d(-200, 200, -200, 200))
    Calling m_sprite.draw(_gc, position.x, position.y); renders the sprite upside down. Walking through the sprite code, it appears to assume a positive-y-down coordinate system.

    I fixed this locally by changing CL_Sprite_Impl::draw_calcs_step2 to subtract t_params1->destHeight from t_params1->pixDestY instead of add. Here is the code that works for me in my coordinate system (will be broken without using a projection like the above)

    Code:
    	// Calculate final destination rectangle points for surface rectangle:
    	if (params2.rotate_angle.to_radians() == 0.0f)
    	{
    		params1.dest_position[0].x = t_params1->pixDestX;
    		params1.dest_position[1].x = t_params1->pixDestX+t_params1->destWidth;
    		params1.dest_position[2].x = t_params1->pixDestX;
    		params1.dest_position[3].x = t_params1->pixDestX+t_params1->destWidth;
    
    		params1.dest_position[0].y = t_params1->pixDestY;
    		params1.dest_position[1].y = t_params1->pixDestY;
    
        // PMD - set this up to subtract the height to support Y is positive going up
    		params1.dest_position[2].y = t_params1->pixDestY-t_params1->destHeight;
    		params1.dest_position[3].y = t_params1->pixDestY-t_params1->destHeight;
    	}
    	else
    	{
    		// Roll
    		params1.dest_position[0].x = calc_rotate_x(t_params1->pixDestX, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
    		params1.dest_position[1].x = calc_rotate_x(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
    
        // PMD - set this up to subtract the height to support Y is positive going up
    		params1.dest_position[2].x = calc_rotate_x(t_params1->pixDestX, t_params1->pixDestY-t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
    		params1.dest_position[3].x = calc_rotate_x(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY-t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
    
    		params1.dest_position[0].y = calc_rotate_y(t_params1->pixDestX, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
    		params1.dest_position[1].y = calc_rotate_y(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
    
        // PMD - set this up to subtract the height to support Y is positive going up
    		params1.dest_position[2].y = calc_rotate_y(t_params1->pixDestX, t_params1->pixDestY-t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
    		params1.dest_position[3].y = calc_rotate_y(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY-t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
    	}
    It might be worth making a destination rectangle at the top of the function and using that. But this is a quick-and-dirty fix (at least until I find if this breaks something else )

  2. #2

    Default

    Also had to update CL_Sprite_Impl::draw_calcs_step1

    Code:
    	// Find top left point of destination rectangle and map rotation hotspot to screen coordinates:
    	if (params2.scale_x == 1.0 && params2.scale_y == 1.0)
    	{
    		t_params1.destWidth = (float)params2.srcWidth;
    		t_params1.destHeight = (float)params2.srcHeight;
    		t_params1.pixDestX = params2.destX-translation_hotspot.x;
    
        // PMD - set this up to add the height to support Y is positive going up
    		t_params1.pixDestY = params2.destY+translation_hotspot.y;
    		t_params1.rotation_hotspot.x += float(t_params1.pixDestX);
    		t_params1.rotation_hotspot.y += float(t_params1.pixDestY);
    	}
    	else
    	{
    		t_params1.destWidth = params2.srcWidth * params2.scale_x;
    		t_params1.destHeight = params2.srcHeight * params2.scale_y;
    		t_params1.pixDestX = params2.destX-translation_hotspot.x * params2.scale_x;
    
        // PMD - set this up to add the height to support Y is positive going up
    		t_params1.pixDestY = params2.destY+translation_hotspot.y * params2.scale_y;
    		t_params1.rotation_hotspot.x = float(t_params1.pixDestX + t_params1.rotation_hotspot.x * params2.scale_x);
    		t_params1.rotation_hotspot.y = float(t_params1.pixDestY + t_params1.rotation_hotspot.y * params2.scale_y);
    	}

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

    Default

    ClanLib coordinate system for sprites and images is top left.

    If the sprite is upside down, maybe it would be easier to simply flip the sprite using
    Code:
     sprite.set_scale( 1.0f, -1.0f );
    You may need to use: sprite.CL_Sprite::set_alignment(origin_bottom_left ), but i am not sure

    But i'm a bit confused why you are using set_projection like this.
    If you are only using it to move the camera. I would leave the coordinate system to the top left, and use gc.set_translate() to translate the ModelView matrix.

    For reference, other functions are available to adjust the ModelView matrix:

    Code:
    void  set_modelview (const CL_Mat4f &matrix) 
      Sets the model view matrix to a new matrix. 
     
    void  mult_modelview (const CL_Mat4f &matrix) 
      Multiplies the passed matrix onto the model view matrix. 
     
    void  push_modelview () 
      Pushes current model view matrix onto the model view stack. 
     
    void  set_translate (float x, float y, float z=0.0) 
      Sets a translate offset matrix, ignoring any earlier model view settings. 
     
    void  mult_translate (float x, float y, float z=0.0) 
      Adds the translate offset. 
     
    void  push_translate (float x, float y, float z=0.0) 
      Push translation offset onto model view stack. 
     
    void  set_rotate (const CL_Angle &angle, float x=0.0, float y=0.0, float z=1.0, bool normalize=true) 
      Sets a rotation matrix, ignoring any earlier model view settings. 
     
    void  mult_rotate (const CL_Angle &angle, float x=0.0, float y=0.0, float z=1.0, bool normalize=true) 
      Adds a rotation matrix to existing model view. 
     
    void  push_rotate (const CL_Angle &angle, float x=0.0, float y=0.0, float z=1.0) 
      Pushes a rotation matrix onto model view stack. 
     
    void  set_scale (float x, float y, float z=1.0) 
      Sets a scale matrix, ignoring any earlier model view settings. 
     
    void  mult_scale (float x, float y, float z=1.0) 
      Adds a scale matrix to existing model view. 
     
    void  push_scale (float x, float y, float z=1.0) 
      Pushes a scale matrix onto model view stack. 
     
    void  pop_modelview () 
      Pops last pushed model view matrix off the stack and makes it the active one.

  4. #4

    Default

    Yeah, you're right. I'm supposed to be using the view to manipulate the camera. It's been quite a while since I worked directly with matrices and a graphics API. I'm pretty rusty. But still, I will be using a custom projection so that I can define my own coordinate system (X positive right, Y positive up, and origin at center of the screen.) When I do UI stuff I'll probably just use a different projection.

    There's 4 solutions to my problem:
    1) Live with Y is positive going down. This is non-intuitive to me. I'd really rather not, particularly since I want my gameplay/physics coordinates to be the same as the rendering coordinates.
    2) Call set_scale(1, -1) on every single sprite. Lame.
    3) Change clanlib to internally init the scale in the sprite to this value. Works but doesn't hide the fact that it is still "wrong" because of my projection should I change that scale for gameplay reasons.
    4) Change sprite's vertex generation code to assume a Y positive going up axis. Allows me to use the coordinate system I want to use without any kludges in the code I'll spend 99% of my time in.

    If I have too much trouble out of this change I'll introduce a multiplier or something to multiply the scale of the sprite right before the vertex generation.

    I'm not faulting clanlib or anything. I've been really impressed with the library so far! The intention of my post was to share this with anyone else who wants Y-positive coordinates and possibly get feedback on if there was an easier way to do this or if maybe this would completely break a LOT of stuff. So I do appreciate your feedback. I tried the GUI example app and it is working fine though this definitely breaks some of the other samples (like shadow of the beast). Oddly enough, the RTS demo works fine though.

    Also found another bug my change above introduces regarding rotation. This fixes it:

    Code:
    	// Find top left point of destination rectangle and map rotation hotspot to screen coordinates:
    	if (params2.scale_x == 1.0 && params2.scale_y == 1.0)
    	{
    		t_params1.destWidth = (float)params2.srcWidth;
    		t_params1.destHeight = (float)params2.srcHeight;
    		t_params1.pixDestX = params2.destX-translation_hotspot.x;
    
        // PMD - set this up to add the height to support Y is positive going up
        //t_params1.pixDestY = params2.destY-translation_hotspot.y;
    		t_params1.pixDestY = params2.destY+translation_hotspot.y;
    		t_params1.rotation_hotspot.x += float(t_params1.pixDestX);
        //t_params1.rotation_hotspot.y += float(t_params1.pixDestY);
    		t_params1.rotation_hotspot.y -= float(t_params1.pixDestY);
    	}
    	else
    	{
    		t_params1.destWidth = params2.srcWidth * params2.scale_x;
    		t_params1.destHeight = params2.srcHeight * params2.scale_y;
    		t_params1.pixDestX = params2.destX-translation_hotspot.x * params2.scale_x;
    
        // PMD - set this up to add the height to support Y is positive going up
    		//t_params1.pixDestY = params2.destY-translation_hotspot.y * params2.scale_y;
        t_params1.pixDestY = params2.destY+translation_hotspot.y * params2.scale_y;
    		t_params1.rotation_hotspot.x = float(t_params1.pixDestX + t_params1.rotation_hotspot.x * params2.scale_x);
    
        //t_params1.rotation_hotspot.y = float(t_params1.pixDestY + t_params1.rotation_hotspot.y * params2.scale_y);
    		t_params1.rotation_hotspot.y = float(t_params1.pixDestY - t_params1.rotation_hotspot.y * params2.scale_y);
    	}
    If you can think of any reason why this change would break things horribly, please let me know. I'll keep my eye on this thread. I really appreciate your suggestions.

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

    Default

    I don't know CL_Sprite in depth, it's an old class, maybe one of the oldest ones.

    For bottom left coordinate system, you can also use:

    Code:
    gc.set_map_mode(cl_map_2d_lower_left);
    Maybe CL_Sprite should contain: "If (gc.get_map_mode() == cl_map_2d_lower_left) { do +y up } else {do +y down}"

    For reference for anyone else reading the thread:

    We have tests for CL_Sprite in Tests/Display/2D, and maybe also in Tests/Display/Sprites1, but I have never used that one.

  6. #6

    Default

    So this also breaks text rendering and is not friendly to one of the Rect's constructors which takes a left and bottom as x/y and adds size.x and size.y to get top and right.

    I'm not going to post any more code updates because I can't really recommend anyone to go this route.. who knows how much other stuff this change breaks.

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

    Default

    The default 2D coordinate system was chosen to be "top left"

    Yes, it does seem counter-intuitive why that was chosen. These are the reasons for and against:

    Top Left
    1) The screen is stored in memory so that the top left pixel is at location 0.
    2) In most languages we read a book from top to bottom.

    Bottom Left
    1) Conventional coordinates, 0,0 is bottom left
    2) When something goes higher (up), you except the Y position to increase.

    In the past (let's say in the early 1990's), the convention was top left.
    So, that's what the clanlib developers used.

    But to please everyone, we added set_map_mode(cl_map_2d_lower_left);

    But this leads onto another question. "What is the baseline of a image?"

    For consistancy, we chose top left again.

    This does cause problems when using different coordinate systems.
    CL_Rectf() expects "left, top, right, bottom" or "left, top, size"
    You cannot easily create a CL_Rectf() with "left, bottom, right, top" or "left, bottom, size)

    I cannot see an easy solution to this problem that will keep everybody happy.

    In my opinion, I think we should use what we already have. It would be too much work (for everyone) to change it now (and fix all the old code)

    (For reference, at work, I use both coordinate systems, but prefer top left).

  8. #8
    Lesser Wizard
    Join Date
    Jun 2007
    Posts
    109

    Default

    Confusion might come from OpenGL standard using the bottom left as the origin point (if I recall correctly).

    I actually like that ClanLib uses the top left for its Sprite Class. It's traditional for 2d.

    Just don't use CL_Sprite if that drives you crazy. I did an entire project without it when I was using raw GL in a 2d environment. There's nothing particularly special about CL_Sprite that forces you to use it. Also, it's kind of funny how instead of fixing your projection you try to fix ClanLib

    The irony of my project was I'm pretty sure I did my own corrections to assume 0,0 is the top left. =)

    But i'm a bit confused why you are using set_projection like this.
    If you are only using it to move the camera. I would leave the coordinate system to the top left, and use gc.set_translate() to translate the ModelView matrix.
    I had a similar thought...

    I wouldn't worry so much about what you're "supposed" to do and what works. I found this to be an easy solution to moving camera + stationary GUI elements. I'm using only CL_Sprites, basically.. Main loop:

    Code:
    while(!quit)
    {
    	timer();
    
    	if(!lock_input)
    		get_input(window.get_ic());
    
    	gui_update();
    	
    	gc.clear(CL_Colorf(0.0f,0.0f,0.0f));
    	gc.set_map_mode(CL_MapMode(cl_map_2d_upper_left));
    
    	if(area.is_initialized())
    	{
    		area.update(micro_second);
    		gc.pop_modelview();
    		gc.set_translate(-area.get_camera().x, -area.get_camera().y);
    		area.draw();
    		gc.pop_modelview();
    		gc.set_translate(area.get_camera().x, area.get_camera().y);
    	}
    
    	gc.pop_modelview();
    	hud_draw();
    
    	// GUI
    	gui.exec(false);
    	gui_draw_windows(gc, wm);
    	if(area.is_initialized())
    		gc.set_translate(-area.get_camera().x, -area.get_camera().y);
    
    	window.flip(0);		// TODO: V-sync toggle
    	CL_System::sleep(0);
    	frames++;
    }
    Kinda brutish, but works...
    Last edited by catch22; 04-05-2010 at 05:44 PM.

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
  2. The upside down problem
    By Judas in forum Official ClanLib SDK Forums
    Replies: 3
    Last Post: 10-17-2010, 03:30 PM
  3. Sprites and more...
    By Ledorax in forum Novashell Game Creation System
    Replies: 8
    Last Post: 09-28-2009, 04:47 PM
  4. Sprites
    By lixopmstp in forum Official ClanLib SDK Forums
    Replies: 4
    Last Post: 01-21-2007, 02:36 AM
  5. sprites
    By seyah3 in forum Dink Smallwood HD
    Replies: 0
    Last Post: 02-03-2006, 03:05 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
  •