Formeln

Saturday, March 23, 2013

The Camera Manager

In the last tutorial we saw, that with a growing number of cameras we need some additional code to manage different cameras. The reasons were: Renderables need access to the current camera, the different cameras share a set of variables and the handling of mouse and keyboard events is for each camera different.

The obvious way to address this problem is either by providing an interface or an abstract class. I chose an abstract class:

Abstract Class Camera


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SlimDX;

namespace Apparat
{
    public abstract class Camera
    {
        public Vector3 eye;
        public Vector3 target;
        public Vector3 up;

        public Matrix view = Matrix.Identity;
        public Matrix perspective = Matrix.Identity;
        public Matrix viewPerspective = Matrix.Identity;

        public Matrix View
        {
            get { return view; }
        }

        public void setPerspective(float fov, float aspect, float znear, float zfar)
        {
            perspective = Matrix.PerspectiveFovLH(fov, aspect, znear, zfar);
        }

        public void setView(Vector3 eye, Vector3 target, Vector3 up)
        {
            view = Matrix.LookAtLH(eye, target, up);
        }

        public Matrix Perspective
        {
            get { return perspective; }
        }

        public Matrix ViewPerspective
        {
            get { return view * perspective; }
        }

        public bool dragging = false;
        public int startX = 0;
        public int deltaX = 0;

        public int startY = 0;
        public int deltaY = 0;

        public abstract void MouseUp(object sender, MouseEventArgs e);
        public abstract void MouseDown(object sender, MouseEventArgs e);
        public abstract void MouseMove(object sender, MouseEventArgs e);
        public abstract void MouseWheel(object sender, MouseEventArgs e);
    }
}

These are the variables and methods all cameras share. Furthermore cameras deriving from this abstract class have to implement the handlers for interacting with the control, like MouseUp. I deleted the corresponding variables and methods from the OrbitCamera and OrbitPanCamera classes did override the handlers. For the sake of brevity I will not post the code here but refer to the source code at the bottom of this tutorial.

CameraManager Class


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Apparat
{
    public class CameraManager
    {
        #region Singleton Pattern
        private static CameraManager instance = null;
        public static CameraManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new CameraManager();
                }
                return instance;
            }
        }
        #endregion

        #region Constructor
        private CameraManager() 
        {
            OrbitPanCamera ocp = new OrbitPanCamera();
            OrbitCamera oc = new OrbitCamera();
            cameras.Add(ocp);
            cameras.Add(oc);

            currentIndex = 0;
            currentCamera = cameras[currentIndex];
        }
        #endregion

        List<camera> cameras = new List<camera>();

        public Camera currentCamera;
        int currentIndex;

        public string CycleCameras()
        {
            int numCameras = cameras.Count;
            currentIndex = currentIndex + 1;
            if (currentIndex == numCameras)
                currentIndex = 0;
            currentCamera = cameras[currentIndex];
            return currentCamera.ToString();
        }
    }
}

The CameraManager is now responsible for creating the cameras and uses the Singleton pattern, as it is the only object, the rest of the engine is talking to, when cameras need to be accessed. Consequently, the OrbitCamera and OrbitPanCamera are not Singletons anymore.

The CameraManager holds a list of cameras, which I populate in its contructor. In order to change the camera, I added the method CycleCameras. The engine can gain access to the current camera via the currentCamera variable with CameraManager.Instance.currentCamera.

RenderControl


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Apparat.Renderables;

namespace Apparat
{
    public partial class RenderControl : UserControl
    {
        public RenderControl()
        {
            InitializeComponent();
            this.MouseWheel += new MouseEventHandler(RenderControl_MouseWheel);
        }

        public void init()
        {
            DeviceManager.Instance.createDeviceAndSwapChain(this);
            RenderManager.Instance.init();

            Grid grid = new Grid(10, 1.0f);
            TriangleEF triangle = new TriangleEF();
            Scene.Instance.addRenderObject(triangle);
            Scene.Instance.addRenderObject(grid);
        }

        public void shutDown()
        {
            RenderManager.Instance.shutDown();
            DeviceManager.Instance.shutDown();
        }

        private void RenderControl_MouseUp(object sender, MouseEventArgs e)
        {
            CameraManager.Instance.currentCamera.MouseUp(sender, e);
        }

        private void RenderControl_MouseDown(object sender, MouseEventArgs e)
        {
            CameraManager.Instance.currentCamera.MouseDown(sender, e);
        }

        private void RenderControl_MouseMove(object sender, MouseEventArgs e)
        {
            CameraManager.Instance.currentCamera.MouseMove(sender, e);
        }

        void RenderControl_MouseWheel(object sender, MouseEventArgs e)
        {
            CameraManager.Instance.currentCamera.MouseWheel(sender, e);
        }

        private void RenderControl_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F1)
            {
                CameraManager.Instance.CycleCameras();
            }
        }
    }
}

In the RenderControl the mouse handlers refer to the current camera of the CameraManger and call
the according handler. Furthermore I use the KeyUp handler and the F1 key to cycle through the cameras.

Renderables

Now the render methods of the Renderables have to updated like in this example, where the ViewPerspective matrix is set via the CameraManager.

public override void render()
{
  Matrix ViewPerspective = CameraManager.Instance.currentCamera.ViewPerspective;
  tmat.SetMatrix(ViewPerspective);

  // configure the Input Assembler portion of the pipeline with the vertex data
  DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
  DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.LineList;
  DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, 12, 0));

  technique = effect.GetTechniqueByName("Render");

  EffectTechniqueDescription techDesc;
  techDesc = technique.Description;

  for (int p = 0; p < techDesc.PassCount; ++p)
  {
    technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
    DeviceManager.Instance.context.Draw(numVertices, 0);
  }
}

Conclusion

When dealing with several cameras a CameraManager is needed to deal with them in a flexible way. This CameraManager will be extended in future tutorials. 

You can download the code for this tutorial here.

No comments:

Post a Comment