Portfolio

C++/Lua Camera

The camera's for Box Bunny and for Deepspace are very similar. The C++ interface only exposes SetAt, SetUp, SetEye, VewToWorld and World To View. Box Bunny exposed a few more, but they complicated both the C++ and Lua code. In Deepspace I decided to limit the C++ to storing the data needed to create and cache the world to view transform.

Using this simple lua interface it's very easy to throw together new cameras.

Interesting code from Camera Lua Interface
local function SetCamera(self, name)
  if(name == "Spherical") then
    self.update_function = SphericalCamera
  elseif(name == "Scroll") then
    self.update_function = ScrollCamera
  end

  local lcam = self
  function camera_update()
    lcam:update_function()
  end
end

function InitCameraComponent(cam_table, component)
  local l_camera = cam_table.camera
  game_camera = component

  component.first_persion = false
  component.radius        = 5.0
  component.azimuth       = 0.0
  component.inclination   = 0.0
  component.scroll_offset = { x = 0; y = 0; z = 20; }
  component.SetCamera     = SetCamera

  function component:SetAt( pos)      l_camera:SetAt( pos) end
  function component:SetEye(pos)      l_camera:SetEye(pos) end
  function component:SetUp( pos)      l_camera:SetUp( pos) end

  function component:GetAt()          return l_camera:GetAt()  end
  function component:GetEye()         return l_camera:GetEye() end
  function component:GetUp()          return l_camera:GetUp()  end

  function component:ViewToWorld(pos) return l_camera:ViewToWorld(pos) end
  function component:WorldToView(pos) return l_camera:WorldToView(pos) end

  -- camera_update is only called from C++
  component.camera_update = _G[cam_table.init_name]
end


Sample Lua Camera
This camera is a simple example for a side scrolling game and can easily be extened to allow the player to move within a bounding box in screenspace without moving the camera
-- This is a camera used side scrolling that puts the lookat location
-- at the entity and the eye at the entity location + self.scroll_offset
local function ScrollCamera(self)
  local parent    = self:GetParent()
  local transform = parent.Transform

  local offset = self.scroll_offset;
  local at     = transform:GetPosition()
  local eye    = { x = at.x + offset.x; y = at.y + offset.y; z = at.z + offset.z; }
  local up     = { x = 0; y = 1; z = 0; }

  self:SetAt(at)
  self:SetEye(eye)
  self:SetUp(up)
end

Interesting Code from Camera.cpp
Most of the code on the C++ is basic get/setters for lua and only one get/set pair is shown here.
namespace Graphics{
  LUA_CLASS_BEGIN(Camera)
    LUA_CLASS_METHOD(Camera, SetAt),
    LUA_CLASS_METHOD(Camera, SetEye),
    LUA_CLASS_METHOD(Camera, SetUp),
    LUA_CLASS_METHOD(Camera, GetAt),
    LUA_CLASS_METHOD(Camera, GetEye),
    LUA_CLASS_METHOD(Camera, GetUp),
    LUA_CLASS_METHOD(Camera, ViewToWorld),
    LUA_CLASS_METHOD(Camera, WorldToView),
  LUA_CLASS_END();

  Camera::Camera(const UT::ComponentInit com_init, const std::string& init_type)
  : UT::Component(com_init, true),
    m_init_type(init_type),
    m_dirty(true),
    m_at(0, 0, 0),
    m_eye(0, 0, 10),
    m_up(0, 1, 0)
  {
  }

  int Camera::SetUp(lua_State* state)
  {
    Lua::State L(state);
    float3 up;
    if(L.Get(1, up)){
      m_up = up;
      m_dirty = true;
    }
    return 0;
  }

  int Camera::GetUp(lua_State* state)
  {
    Lua::State L(state);
    L.Push(m_up);
    return 1;
  }
  
  int Camera::ViewToWorld(lua_State* state)
  {
    Lua::State L(state);
    float3 init_pos;
    if(L.Get(1, init_pos)){
      float3 out_pos;
      float4x4 inv_view_mat;
      Math::Inverse(inv_view_mat, GetLookAt());
      Math::TransformPoint(out_pos, init_pos, inv_view_mat);
      L.Push(out_pos);
      return 1;
    }
    return 0;
  }

  int Camera::WorldToView(lua_State* state)
  {
    Lua::State L(state);
    float3 init_pos;
    if(L.Get(1, init_pos)){
      float3 out_pos;
      Math::TransformPoint(out_pos, init_pos, GetLookAt());
      L.Push(out_pos);
      return 1;
    }
    return 0;
  }

  const float4x4& Camera::GetLookAt() const
  {
    if(m_lua_state){
      m_lua_state->GetGlobal("camera_update");
      m_lua_state->Call(0, 0, "camera_update");
    }

    if(m_dirty){
      Math::LookAt(m_look_at, m_eye, m_at, m_up);
      m_dirty = false;
    }
    return m_look_at;
  }

  void Camera::PushLua(Lua::State& L)
  {
    lua_createtable(L, 0, 2);
    L.Push(this);
    L.SetField(-2, "camera");
    L.Push(m_init_type);
    L.SetField(-2, "init_name");

    L.GetGlobal("InitCameraComponent");

    m_lua_state = &L;
  }