module Mittsu class PerspectiveCamera < Camera attr_accessor :zoom, :fov, :aspect, :near, :far def initialize(fov = 50.0, aspect = 1.0, near = 0.1, far = 2000.0) super() @type = 'PerspectiveCamera' @zoom = 1.0 @fov = fov.to_f @aspect = aspect.to_f @near = near.to_f @far = far.to_f @full_width = nil update_projection_matrix end # Uses Focal Length (in mm) to estimate and set FOV # 35mm (fullframe) camera is used if frame size is not specified; # Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html def set_lens(focal_length, frame_height = 24.0) @fov = 2.0 * Math.rad_to_deg(Math.atan(frame_height / (focal_length * 2.0))) update_projection_matrix end # Sets an offset in a larger frustum. This is useful for multi-window or # multi-monitor/multi-machine setups. # # For example, if you have 3x2 monitors and each monitor is 1920x1080 and # the monitors are in grid like this # # +---+---+---+ # | A | B | C | # +---+---+---+ # | D | E | F | # +---+---+---+ # # then for each monitor you would call it like this # # var w = 1920; # var h = 1080; # var fullWidth = w * 3; # var fullHeight = h * 2; # # --A-- # camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); # --B-- # camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); # --C-- # camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); # --D-- # camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); # --E-- # camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); # --F-- # camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); # # Note there is no reason monitors have to be the same size or in a grid. def set_view_offset(full_width, full_height, x, y, width, height) @full_width = full_width @full_height = full_height @x = x @y = y @width = width @height = height update_projection_matrix end def update_projection_matrix new_fov = Math.rad_to_deg(2.0 * Math.atan(Math.tan(Math.deg_to_rad(@fov) * 0.5) / @zoom)) if @full_width aspect = @full_width / @full_height top = Math.tan(Math.deg_to_rad(new_fov * 0.5)) * @near bottom = -top left = aspect * bottom right = aspect * top width = (right - left).abs height = (top - bottom).abs projection_matrix.make_frustum( left + @x * width / @full_width, left + (@x + @width) * width / @full_width, top - (@y + @height) * height / @full_height, top - @y * height / @full_height, @near, @far ) else projection_matrix.make_perspective(new_fov, @aspect, @near, @far) end end def clone camera = Mittsu::PerspectiveCamera.new super(camera) camera.zoom = zoom camera.fov = fov camera.aspect = aspect camera.near = near camera.far = far camera.projection_matrix.copy(projection_matrix) camera end protected def jsonify data = super data[:fov] = self.fov data[:aspect] = self.aspect data[:near] = self.near data[:far] = self.far data end end end