Page 1 of 1

Camera to view all objects

Posted: Sun Mar 27, 2011 8:03 pm
by leiradel
Hi All,

I'm developing a game where all objects have a getExtents() method which returns the minimum and maximum coordinates occupied by the object. I'm trying to develop a camera that will get the extents of all visible objects and scale and translate things appropriately to have them all appear on the screen. I've tried many things but can't seem to make it work:

Code: Select all

function love.draw()
  -- the background is not scaled nor translated 
  love.graphics.draw( back, 0, 0 )

  -- scale and translate the coordinate system
  local minx, miny, maxx, maxy = self.visible[ 1 ]:getExtents()
  local len = #self.tracked
  
  for i = 2, len do
    local ominx, ominy, omaxx, omaxy = self.visible[ i ]:getExtents()
    
    if ominx < minx then
      minx = ominx
    end
    
    if ominy < miny then
      miny = ominy
    end
    
    if omaxx > maxx then
      maxx = omaxx
    end
    
    if omaxy > maxy then
      maxy = omaxy
    end
  end
  
  local dx = maxx - minx
  local dy = maxy - miny
  local scale
  
  if dx > dy then
    scale = love.graphics.getWidth() / dx
  else
    scale = love.graphics.getHeight() / dy
  end
  
  love.graphics.translate( -minx, -miny )
  love.graphics.scale( scale )

  -- draw the objects
  for i = 1, len do
    self.visible[ i ]:draw()
  end
end
Any tips on what I'm doing wrong?

Thanks,

Andre

EDIT: I've found that if I swap the translate and scale calls it works, I have no idea how I missed it. The problem that I'm having now is to centralize the objects on the screen, i.e. if the objects are spread along the horizontal ax I want to make them appear with the same space left on the top and on the bottom.

Re: Camera to view all objects

Posted: Sun Mar 27, 2011 11:22 pm
by BlackBulletIV
I can't see where you're calling push() and pop(). Try this:

Code: Select all

  love.graphics.push()
  love.graphics.scale( scale )
  love.graphics.translate( -minx, -miny )
  
  -- draw the objects
  for i = 1, len do
    self.visible[ i ]:draw()
  end
  
  love.graphics.pop()

Re: Camera to view all objects

Posted: Sun Mar 27, 2011 11:35 pm
by leiradel
BlackBulletIV wrote:I can't see where you're calling push() and pop()
Translation, scaling and rotation only last until love.draw exits. Anyway, I've already added push and pop to print some debug info on the screen.

Thanks,

Andre

Re: Camera to view all objects

Posted: Mon Mar 28, 2011 12:45 am
by BlackBulletIV
Oh so you don't actually need push() and pop()? I thought you did. Well I learned something new. :)

Re: Camera to view all objects

Posted: Mon Mar 28, 2011 2:01 pm
by vrld
leiradel wrote:The problem that I'm having now is to centralize the objects on the screen, i.e. if the objects are spread along the horizontal ax I want to make them appear with the same space left on the top and on the bottom.
You are nearly there. Consider this image:
Camera center
Camera center
camera_center.png (12.04 KiB) Viewed 1765 times
You already calculated the upper left (ul) and lower right (lr) corners:

Code: Select all

ul = (minx, miny)
lr = (maxx, maxy)
You then translate to (-minx, -miny), which is equivalent of moving the upper left corner to the origin (0); but to center the scene, you need to move the point c to the origin.
How do you get c? Simple: It's halfway from ul to lr, i.e.

Code: Select all

c = (ul + lr) / 2 = ( (minx + maxx)/2, (miny + maxy)/2 )
If you do this you'll notice that only a quarter of your objects will be drawn. That's because LÖVE's coordinate system has it's origin in the upper left edge of the screen rather than the screen center. You can compensate for that by moving the upper left edge of the screen to the center, i.e. translate by (-screen_width/2, -screen_height/2).

Re: Camera to view all objects

Posted: Mon Mar 28, 2011 6:31 pm
by leiradel
Thanks vrld, for a nice explanation (with graphics!)

I've managed to make it work:

Code: Select all

  local dx = self.maxx - self.minx
  local dy = self.maxy - self.miny
  local scale, x0, y0
  
  if dx > dy then
    scale = love.graphics.getWidth() / dx
    x0 = -self.minx
    y0 = -self.miny - ( dy - love.graphics.getHeight() / scale ) / 2
  else
    scale = love.graphics.getHeight() / dy
    x0 = -self.minx - ( dx - love.graphics.getWidth() / scale ) / 2
    y0 = -self.miny
  end
  
  love.graphics.scale( scale )
  love.graphics.translate( x0, y0 )
I'm pretty sure I can get away with love.graphics.get[Width|Height] / scale if I add an additional translate call before the scale call.

Thanks again,

Andre