Page 1 of 1

cam11 - Yet another camera library

Posted: Mon Nov 11, 2019 1:06 am
by pgimeno
Having gamera, hump.camera and STALKER-X around, one may wonder, does the world really need yet another camera library?

No, it really doesn't. Unless... ok, ok, not that either. Or, well, maybe a little, if you are like me, and prefer that the guts of your libraries are as optimal as they can be. And that's what this library is. It isn't really full of features, but what it does, it does well, and fast, by leveraging the new built-in Transform object, and a lazy evaluation technique. At the time of writing this, none of the aforementioned libraries has been updated to use this new 11.0 feature.

It can also serve as an engine for a more powerful camera. While I have plans to build a more sophisticated camera library on top of it, these plans may not be realized until I actually have a need for something like that.

Not everything is advantages, though. The LÖVE Transform object uses single-precision floats internally, therefore the results incur a precision penalty with respect to libraries that perform the transformations in pure Lua.

NotABug repo and docs: https://notabug.org/pgimeno/cam11

Re: cam11 - Yet another camera library

Posted: Mon Nov 11, 2019 8:47 am
by zorg
Hi!

Apart from this one tiny documentation bug i found: (should be corner or position instead, no?)
viewport_x and viewport_y are the coordinates of the top left -->angle<-- of the viewport where the camera will be rendered, in screen coordinates. The default is 0 for both.
This looks to be a neat lib; i'm guessing you tested its efficiency against the other 3 libs you mentioned? :3
Also, i do sincerely believe that yes, the world indeed does need more camera libraries.

Re: cam11 - Yet another camera library

Posted: Mon Nov 11, 2019 12:40 pm
by pgimeno
Thanks zorg, that's a false friend that slipped in :) "ángulo" also means "corner" in Spanish.

To be honest, I hadn't tested them. Now I have. Here's the test program:

Code: Select all

local cam11 = require 'cam11'
local stalker = require 'stalker-x'
local gamera = require 'gamera'
local humpcamera = require 'hump_camera'


local getTime = love.timer.getTime

function love.keypressed(k)
  return k == "escape" and love.event.quit()
end

local ntimes = 1e7

--[.[
print('draw() test')
print('===========')
print()

local function dummy() end

local function test(cam, suite)
  local start = getTime()
  for i = 1, ntimes do
    cam:drawtest(dummy)
  end
  local finish = getTime()
  print(suite .. ': '.. finish - start)
end

local cam

-- Monkey-patch the objects to add a draw function to those that don't have
-- one, avoiding naming conflicts (stalker.draw is very different to
-- humpcamera.draw and gamera.draw)
cam11.drawtest = function (cam, fn)
  cam:attach(false)
  fn()
  cam:detach()
end
test(cam11.new(), 'cam11 (no clipping)', false)

cam = stalker.new()
getmetatable(cam).__index.drawtest = cam11.drawtest
test(cam, 'STALKER-X (no clipping)')

cam = humpcamera.new()
getmetatable(cam).__index.drawtest = function (cam, fn)
  cam:draw(0,0,0,0, false, fn)
end
test(humpcamera(), 'hump.camera (no clipping)')


print()


cam11.drawtest = function (cam, fn)
  cam:attach(true)
  fn()
  cam:detach()
end
test(cam11.new(), 'cam11 (clipping)', true)

gamera.drawtest = function(cam, fn)
  cam:draw(fn)
end
test(gamera.new(0, 0, 8192, 8192), 'gamera (clipping)')

humpcamera.drawtest = function (cam, fn)
  cam:draw(0,0,0,0, true, fn)
end
test(humpcamera(), 'hump.camera (clipping)')

print()
print()
--]]

print('Screen to World coords conversion test')
print('======================================')
print()

test = function (cam, fn, suite)
  local start = getTime()
  for i = 1, ntimes do
    fn(cam, i, i)
  end
  local finish = getTime()
  print(suite .. ': '.. finish - start)
end

cam = cam11()
test(cam, cam.toWorld, 'cam11')

cam = stalker()
test(cam, cam.toWorldCoords, 'stalker')

cam = gamera.new(0, 0, 8192, 8192)
test(cam, cam.toWorld, 'gamera')

cam = humpcamera()
test(cam, cam.worldCoords, 'hump.camera')

print()
print()

print('World to Screen coords conversion test')
print('======================================')
print()

cam = cam11()
test(cam, cam.toScreen, 'cam11')

cam = stalker()
test(cam, cam.toCameraCoords, 'stalker')

cam = gamera.new(0, 0, 8192, 8192)
test(cam, cam.toScreen, 'gamera')

cam = humpcamera()
test(cam, cam.cameraCoords, 'hump.camera')

print()
print()

print('setAngle test')
print('=============')
print()

cam = cam11()
test(cam, cam.setAngle, 'cam11')

cam = gamera.new(0, 0, 8192, 8192)
test(cam, cam.setAngle, 'gamera')

cam = humpcamera()
test(cam, cam.rotateTo, 'hump.camera')

love.event.quit()
And here are the results: UPDATE: The results for Screen to World and World to Screen are outdated! See two posts below for updated results that include the new performance improvements.

Code: Select all

draw() test
===========

cam11 (no clipping): 1.7323464825749
STALKER-X (no clipping): 4.054413462989
hump.camera (no clipping): 8.8572070803493

cam11 (clipping): 5.0095141148195
gamera (clipping): 7.060666478239
hump.camera (clipping): 8.8429979169741


Screen to World coords conversion test
======================================

cam11: 0.57571997307241
stalker: 0.53201641887426
gamera: 0.19846635125577
hump.camera: 1.7385331876576


World to Screen coords conversion test
======================================

cam11: 0.56872890982777
stalker: 0.53778914362192
gamera: 0.19814178906381
hump.camera: 1.726593574509


setAngle test
=============

cam11: 0.13578468654305
gamera: 0.92074527684599
hump.camera: 0.13101442158222
Note that they are not entirely fair because of the different feature sets of each library. STALKER-X does not support camera rotation, and does not support clipping. gamera does not have attach/detach methods, therefore the comparison had to be done against artificially created draw() methods which have the potential of offsetting the comparison.

The big surprise came with the point transformation functions. gamera's technique of caching the sine and cosine of the angle pays off, outperforming all others by a wide margin. STALKER-X's lack of support for rotation makes the comparison with cam11 a bit unfair, beating cam11 by a tight margin. I imagine that the overhead of crossing the C++ boundary is what makes the performance of cam11 drop under the expectations. hump.camera is handicapped by its calls to love.graphics.getWidth and love.graphics.getHeight on each call to the transformation functions. It supports undocumented parameters that could solve that, but calling it with undocumented values sounded unfair.

I added the comparison of setAngle() to highlight the caching performed by gamera, but it's not really an important metric for measuring the performance of the lib. It's more usual to need to transform many points than to need to call setAngle() many times.

Re: cam11 - Yet another camera library

Posted: Mon Nov 11, 2019 4:33 pm
by zorg
And funnily enough, corner is synonymous with ankle here... :awesome:

One more question; i've been working on a pretty complex camera lib as well (based on both the Scroll Back talk/article on gamasutra, and the tech demo of the dev who made Insanely Twisted Shadow Planet); i've already asked the other cam. lib. authors if they were fine with me adapting some ideas from their libraries, so i wanted to do the same and ask you as well. (Due credit given, of course)

Re: cam11 - Yet another camera library

Posted: Mon Nov 11, 2019 10:09 pm
by pgimeno
Yeah, sure thing. Feel free to grab any ideas you find interesting.

I've updated the library to make the speed be more on par with gamera. I had to disable hump testing, as it seems to interrupt tracing (because of the love.graphics.getWidth/Height call I presume).

JIT off:

Code: Select all

Screen to World coords conversion test
======================================

cam11: 0.93761968333274
stalker: 1.3281588135287
gamera: 0.75724026188254


World to Screen coords conversion test
======================================

cam11: 0.78163079824299
stalker: 1.3142171455547
gamera: 0.76074439007789
JIT on:

Code: Select all

Screen to World coords conversion test
======================================

cam11: 0.0028397468850017
stalker: 0.0028160996735096
gamera: 0.0028035007417202


World to Screen coords conversion test
======================================

cam11: 0.0028062574565411
stalker: 0.0028091045096517
gamera: 0.0027997028082609
[Edit: I consider the latter results identical, as they are within the noise level]

I'll update the OP and the benchmark post.

Re: cam11 - Yet another camera library

Posted: Wed Dec 04, 2019 4:30 am
by YoungNeer
pgimeno wrote: Mon Nov 11, 2019 1:06 am Having gamera, hump.camera and STALKER-X around, one may wonder, does the world really need yet another camera library?
You are forgetting Brady. It's made by the same guy who made Walt and mlib!