/*****************************************************************
 * CodeName: VJProjector.Net
 * Copyright: Virtual Media Systems, S.L. 2003
 * Assembly: LibGFX
 * Type: C# Source Code
 * Version: 1.0
 * Description: Camera
 * 
 * Revisions
 * ------------------------------------------------
 * [F] 08/03/2003, Jcl - Shaping the thing up
 * 
 *****************************************************************/
 
using System;
using Tao.OpenGl;
using VJProjector.GFX.Immerse.Math;
using VJProjector.GFX.Immerse.Types;
 
namespace VJProjector.GFX.Immerse.Render
{
  using Math = System.Math;
 
  public struct Plane 
  {
    private Vector normal;
    private float distance;
    public float X { get { return normal.X; } set { normal.X = value; } }
    public float Y { get { return normal.Y; } set { normal.Y = value; } }
    public float Z { get { return normal.Z; } set { normal.Z = value; } }
    public float D { get { return distance; } set { distance = value; } }
    public Vector Normal { get { return normal ; } set { normal  = value; } }
    public float Distance { get { return distance; } set { distance = value; } }
    public Plane(float _x, float _y, float _z, float _d) 
    {
      normal = new Vector(_x, _y, _z);
      distance = _d;
      Normalize();
    }
    public void Normalize() 
    {
      float t = normal.Length;
      if(t != 0) 
      {
        normal/=t;
        distance/=t;
      } 
      else 
      {
        normal = new Vector(0,1,0);
        distance = 0;
      }
    }
  }
 
  public class Frustum 
  {
    private Plane [] planes = new Plane[6];
    public Plane [] Planes { get { return planes; } }
    public void GetPlanes() 
    {
      float [] mv = new float[16];
      float [] pj = new float[16];
      Gl.glGetFloatv(Gl.GL_PROJECTION_MATRIX, pj);
      Gl.glGetFloatv(Gl.GL_MODELVIEW_MATRIX, mv);
      GetPlanes(mv, pj);
    }
 
    public void GetPlanes(float [] mv, float [] pj) 
    {
      float [] clp = new float[16];
      //Matrix clp = pj * mv; /** Expanded **/
      clp[ 0] = mv[ 0] * pj[ 0] + mv[ 1] * pj[ 4] + mv[ 2] * pj[ 8] + mv[ 3] * pj[12];
      clp[ 1] = mv[ 0] * pj[ 1] + mv[ 1] * pj[ 5] + mv[ 2] * pj[ 9] + mv[ 3] * pj[13];
      clp[ 2] = mv[ 0] * pj[ 2] + mv[ 1] * pj[ 6] + mv[ 2] * pj[10] + mv[ 3] * pj[14];
      clp[ 3] = mv[ 0] * pj[ 3] + mv[ 1] * pj[ 7] + mv[ 2] * pj[11] + mv[ 3] * pj[15];
 
      clp[ 4] = mv[ 4] * pj[ 0] + mv[ 5] * pj[ 4] + mv[ 6] * pj[ 8] + mv[ 7] * pj[12];
      clp[ 5] = mv[ 4] * pj[ 1] + mv[ 5] * pj[ 5] + mv[ 6] * pj[ 9] + mv[ 7] * pj[13];
      clp[ 6] = mv[ 4] * pj[ 2] + mv[ 5] * pj[ 6] + mv[ 6] * pj[10] + mv[ 7] * pj[14];
      clp[ 7] = mv[ 4] * pj[ 3] + mv[ 5] * pj[ 7] + mv[ 6] * pj[11] + mv[ 7] * pj[15];
 
      clp[ 8] = mv[ 8] * pj[ 0] + mv[ 9] * pj[ 4] + mv[10] * pj[ 8] + mv[11] * pj[12];
      clp[ 9] = mv[ 8] * pj[ 1] + mv[ 9] * pj[ 5] + mv[10] * pj[ 9] + mv[11] * pj[13];
      clp[10] = mv[ 8] * pj[ 2] + mv[ 9] * pj[ 6] + mv[10] * pj[10] + mv[11] * pj[14];
      clp[11] = mv[ 8] * pj[ 3] + mv[ 9] * pj[ 7] + mv[10] * pj[11] + mv[11] * pj[15];
 
      clp[12] = mv[12] * pj[ 0] + mv[13] * pj[ 4] + mv[14] * pj[ 8] + mv[15] * pj[12];
      clp[13] = mv[12] * pj[ 1] + mv[13] * pj[ 5] + mv[14] * pj[ 9] + mv[15] * pj[13];
      clp[14] = mv[12] * pj[ 2] + mv[13] * pj[ 6] + mv[14] * pj[10] + mv[15] * pj[14];
      clp[15] = mv[12] * pj[ 3] + mv[13] * pj[ 7] + mv[14] * pj[11] + mv[15] * pj[15];
 
 
      // right plane
      planes[0] = new Plane(
        clp[3]-clp[0], 
        clp[7]-clp[4],
        clp[11]-clp[8],
        clp[15]-clp[12]);
      // left plane
      planes[1] = new Plane(
        clp[3]+clp[0], 
        clp[7]+clp[4],
        clp[11]+clp[8],
        clp[15]+clp[12]);
      // bottom plane
      planes[2] = new Plane(
        clp[3]+clp[1], 
        clp[7]+clp[5],
        clp[11]+clp[9],
        clp[15]+clp[13]);
      // top plane
      planes[3] = new Plane(
        clp[3]-clp[1], 
        clp[7]-clp[5],
        clp[11]-clp[9],
        clp[15]-clp[13]);
      // far plane
      planes[4] = new Plane(
        clp[3]-clp[2], 
        clp[7]-clp[6],
        clp[11]-clp[10],
        clp[15]-clp[14]);
      // near plane
      planes[5] = new Plane(
        clp[3]+clp[2], 
        clp[7]+clp[6],
        clp[11]+clp[10],
        clp[15]+clp[14]);
 
    }
    public bool PointInFrustum(Vector v) 
    {
      foreach(Plane p in planes) 
        if(p.Normal.Dot(v)+p.Distance <=0)
          return false;
      return true;
    }
    public bool PointInFrustum(float x, float y, float z) 
    {
      foreach(Plane p in planes) 
        if(p.X * x + p.Y * y + p.Z * z + p.D <=0)
          return false;
      return true;
    }
 
    public bool SphereInFrustum(BoundingSphereEX sph) 
    {
      foreach(Plane p in planes) 
        if(p.Normal.Dot(sph.Center)+p.Distance <= -sph.Radius)
          return false;
      return true;
    }
    public bool SphereInFrustum(Vector pos, float radius) 
    {
      foreach(Plane p in planes) 
        if(p.Normal.Dot(pos)+p.Distance <= -radius)
          return false;
      return true;
    }
    public bool SphereInFrustum(float x, float y, float z, float radius)
    {
      foreach(Plane p in planes) 
        if(p.D * x + p.Y * y + p.Z * z + p.D <= -radius)
          return false;
      return true;
    }
 
    public float SphereInFrustumDistance(BoundingSphereEX sph) 
    {
      return SphereInFrustumDistance(sph.CenterX, sph.CenterY, sph.CenterZ, sph.Radius);
    }    
    public float SphereInFrustumDistance(Vector pos, float radius) 
    {
      return SphereInFrustumDistance(pos.X, pos.Y, pos.Z, radius);
    }
    public float SphereInFrustumDistance(float x, float y, float z, float radius) 
    {
      float d = 0;
      foreach(Plane p in planes) 
      {
        d = p.X * x + p.Y * y + p.Z * z + p.D;
        if(d <= - radius)
          return 0;
      }
      return d+radius;
    }
  }
 
 
  public enum CameraTypes
  {
    FirstPerson = 0,
    Directional = 1,
    Target = 2,
    Orthogonal = 3,
  }
 
  public enum FovTypes
  {
    Horizontal = 0,
    Vertical,
  }
 
  public class Camera : BaseNode
	{
    public static Camera OrthoFill
    {
      get 
      {
        Camera c = new Camera();
        c.CameraMode = CameraTypes.Orthogonal;
        c.OrthogonalCoords = new RectEX(0,1,1,0);
        c.CacheProjectionMatrix = false;
        c.CacheWorldMatrix = false;
        c.AspectRatioFromViewport = false;
        c.AspectRatio = 800/600;
        c.ZNear = -5;
        c.ZFar = 5;
        return c;
      }
    }
    public static Camera Screen2D_800x600 
    {
      get 
      {
        Camera c = new Camera();
        c.CameraMode = CameraTypes.Orthogonal;
        c.OrthogonalCoords = new RectEX(0,600,800,0);
        c.CacheProjectionMatrix = false;
        c.CacheWorldMatrix = false;
        c.AspectRatioFromViewport = false;
        c.AspectRatio = 800/600;
        c.ZNear = -5;
        c.ZFar = 5;
        return c;
      }
    }
    public static Camera Screen2D_1024x768 
    {
      get 
      {
        Camera c = new Camera();
        c.CameraMode = CameraTypes.Orthogonal;
        c.OrthogonalCoords = new RectEX(0,768,1024,0);
        c.CacheProjectionMatrix = false;
        c.CacheWorldMatrix = false;
        c.AspectRatioFromViewport = false;
        c.AspectRatio = 1024/768;
        c.ZNear = -5;
        c.ZFar = 5;
        return c;
      }
    }
    public static Camera Screen2D_Viewport
    {
      get 
      {
        Camera c = new Camera();
        c.CameraMode = CameraTypes.Orthogonal;
        c.AspectRatioFromViewport = true;
        c.OrthogonalCoordsFromViewport = true;
        c.CacheProjectionMatrix = false;
        c.CacheWorldMatrix = false;
        c.ZNear = -5;
        c.ZFar = 5;
        return c;
      }
    }
    public static Camera Screen2D(float width, float height) 
    {
      Camera c = new Camera();
      c.CameraMode = CameraTypes.Orthogonal;
      c.OrthogonalCoordsFromViewport = false;
      c.OrthogonalCoords = new RectEX(0,height,width,0);
      c.CacheProjectionMatrix = false;
      c.CacheWorldMatrix = false;
      c.AspectRatioFromViewport = false;
      c.AspectRatio = width/height;
      c.ZNear = -5;
      c.ZFar = 5;
      return c;
    }
 
    private Frustum viewFrustum = new Frustum();
    private Vector cameraPosition = new Vector(1,1,1);
    private Vector targetPosition = new Vector(0,0,0);
    private Vector upVector = new Vector(0,1,0);    
    private Quaternion rotation = Quaternion.Identity;
    private Vector scale = new Vector(1,1,1);
    private float fov = 45;
    private FovTypes fovType = FovTypes.Vertical;
    private float zFar = 100.0f, zNear = 0.1f;
 
    private Matrix projectionMatrix = Matrix.Identity;
    private Matrix worldMatrix = Matrix.Identity;
    private bool cacheWorldMatrix = false;
    private bool cacheProjectionMatrix = false;
    private bool resetWorldMatrix = true;
    private bool resetProjectionMatrix = true;
 
    private CameraTypes cameraType = CameraTypes.Target;
 
    private bool aspectRatioFromViewport = false;
    private double aspectRatio = 4.0/3.0; //(4:3 default aspect ratio)
 
    private RectEX orthoCoords = new RectEX(0,0,800,600);
    private bool orthoCoordsFromViewport = false;
 
    // Public properties
    public Vector CameraPosition { get { return cameraPosition; } set { cameraPosition = value; resetWorldMatrix = true; } }
    public Vector TargetPosition { get { return targetPosition; } set { targetPosition = value; resetWorldMatrix = true; } }
    public Vector UpVector { get { return upVector; } set { upVector=value; resetWorldMatrix = true; } }
    public Quaternion Rotation { get { return rotation ; } set { rotation= value; resetWorldMatrix = true; } }
    public Vector Scale { get { return scale; } set { scale= value; resetWorldMatrix = true; } }
    public float FOV { get { return fov; } set { fov= value; resetProjectionMatrix = true; } }
    public FovTypes FOVType { get { return fovType; } set { fovType= value; resetProjectionMatrix = true; } }
    public float ZFar { get { return zFar; } set { zFar= value; resetProjectionMatrix = true; } }
    public float ZNear { get { return zNear; } set { zNear= value; resetProjectionMatrix = true; } }
    public bool CacheWorldMatrix { get { return cacheWorldMatrix; } set { cacheWorldMatrix = value; resetWorldMatrix = true; } }
    public bool CacheProjectionMatrix { get { return cacheProjectionMatrix; } set { cacheProjectionMatrix = value; resetProjectionMatrix = true; } }
    public double AspectRatio { get { return aspectRatio; } set { aspectRatio = value; } }
    public bool  AspectRatioFromViewport { get { return aspectRatioFromViewport; } set { aspectRatioFromViewport = value; } }
    public RectEX OrthogonalCoords { get { return orthoCoords; } set { orthoCoords = value; resetProjectionMatrix = true; } }
    public bool OrthogonalCoordsFromViewport { get { return orthoCoordsFromViewport; } set { orthoCoordsFromViewport = value; resetProjectionMatrix = true; } }
    public Frustum ViewFrustum { get { return viewFrustum; } }
 
    public CameraTypes CameraMode
    { 
      get { 
        return cameraType; 
      } 
      set { 
        if(value!=cameraType) 
        { 
          cameraType = value; 
          resetWorldMatrix = true; 
          resetProjectionMatrix = true; 
        } 
      } 
    }
 
    public Matrix ProjectionMatrix                               
    { 
      get 
      { 
        if(cacheProjectionMatrix && !resetProjectionMatrix) 
        {          
          return projectionMatrix;
        } 
        else 
        {
          float [] fr = new float[16];
          Matrix r;
          int p;
          Gl.glGetIntegerv(Gl.GL_MATRIX_MODE, out p);
          Gl.glMatrixMode(Gl.GL_PROJECTION);
          Gl.glPushMatrix();
          SetProjectionMatrix();
          if(cacheProjectionMatrix) 
          {
            r = projectionMatrix;
          }
          else 
          {
            float [] f = new float[16];
            Gl.glGetFloatv(Gl.GL_PROJECTION_MATRIX,f);
            r = new Matrix(f);
          }
          Gl.glPopMatrix();
          Gl.glMatrixMode(p);
          return r;
        }        
      }
      set 
      {
        projectionMatrix = value;
        resetProjectionMatrix = true;
      }
    }
 
    public Matrix WorldMatrix 
    { 
      get 
      { 
        if(cacheWorldMatrix && !resetWorldMatrix) 
        {          
          return WorldMatrix;
        } 
        else 
        {
          float [] fr = new float[16];
          Matrix r;
          Gl.glGetFloatv(Gl.GL_MODELVIEW_MATRIX, fr);
          SetWorldMatrix();
          if(cacheWorldMatrix) 
          {
            r = WorldMatrix;
          }
          else 
          {
            float [] f = new float[16];
            Gl.glGetFloatv(Gl.GL_MODELVIEW_MATRIX,f);
            r = new Matrix(f);
          }
          int p;
          Gl.glGetIntegerv(Gl.GL_MATRIX_MODE, out p);
          Gl.glMatrixMode(Gl.GL_MODELVIEW);
          Gl.glLoadMatrixf(fr);
          Gl.glMatrixMode(p);
          return r;
        }        
      }
      set 
      {
        worldMatrix = value;
        resetWorldMatrix = true;
      }
    }
 
    public Camera() : base("Unnamed Camera", "Camera")
    {
    }
 
    public float FrustumMultiplierX=0, FrustumMultiplierY=0;
 
    private void setHorizontalFov() 
    {
      double xmin, xmax, ymin, ymax;
      double aspect;
 
      if(zNear == 0) zNear=0.01f;
      if(aspectRatioFromViewport) 
      {
        float [] param = new float[4];
        Gl.glGetFloatv(Gl.GL_VIEWPORT, param);	
        aspect = param[2]/param[3];
      } 
      else 
      {
        aspect = aspectRatio;
      }
      ymax = (double)zNear * Math.Tan((double)fov * Math.PI / 360.0);
      ymin = -ymax;
      xmin = ymin / aspect;
      xmax = ymax / aspect;
 
      double frSepX = 2.0 * (ymax)*(double)FrustumMultiplierX; //(float)i*FrustumSepX;
      double frSepY = 2.0 * (xmax)*(double)FrustumMultiplierY; //(float)i*FrustumSepX;
      Gl.glFrustum(ymin+frSepX, ymax+frSepX, xmin+frSepY, xmax+frSepY, zNear, zFar);
    }
 
    private void setVerticalFov() 
    {
      double aspect;
      if(aspectRatioFromViewport) 
      {
        float [] param = new float[4];
        Gl.glGetFloatv(Gl.GL_VIEWPORT, param);	
        aspect = param[2]/param[3];
      } 
      else 
      {
        aspect = aspectRatio;
      }
      Glu.gluPerspective((double)fov,aspect,(double)zNear+0.00001, (double)zFar);
    }
 
    private void setOrtho() 
    {
      Gl.glLoadIdentity();
      if(!orthoCoordsFromViewport) 
      {
        Gl.glOrtho(orthoCoords.X1,orthoCoords.X2, orthoCoords.Y1, orthoCoords.Y2, zNear,zFar);
      }
      else 
      {   
        float [] param = new float[4];
        Gl.glGetFloatv(Gl.GL_VIEWPORT, param);	
        Gl.glOrtho(0, param[2], param[3], 0, zNear,zFar);
        orthoCoords.X1 = 0; 
        orthoCoords.X2 = param[2]; 
        orthoCoords.Y1 = param[3]; 
        orthoCoords.Y2 = 0; 
      }
    }
 
    private void SetProjectionMatrix() 
    {
      Gl.glMatrixMode(Gl.GL_PROJECTION);
      if(resetProjectionMatrix) 
      {
        Gl.glLoadIdentity();
        if(cameraType != CameraTypes.Orthogonal) 
        {
          switch(fovType) 
          {
            case FovTypes.Horizontal:
              setHorizontalFov();
              break;
            case FovTypes.Vertical:
              setVerticalFov();
              break;
          }
        } 
        else 
        {
          setOrtho();
        }        
        // Caché projection matrix
        if(cacheProjectionMatrix) 
        {
          float [] f = new float[16];
          Gl.glGetFloatv(Gl.GL_PROJECTION_MATRIX,f);
          projectionMatrix = new Matrix(f);
          resetProjectionMatrix = false;
        }
      } 
      else 
      {
        Gl.glLoadMatrixf(projectionMatrix);
      }
    }
 
    private void SetPickingProjectionMatrix(int cX, int cY, int bsX, int bsY) 
    {
      Gl.glMatrixMode(Gl.GL_PROJECTION);
      Gl.glLoadIdentity();
      int [] viewport = new int[4];
      Gl.glGetIntegerv(Gl.GL_VIEWPORT, viewport);
      Glu.gluPickMatrix(cX, viewport[3]-cY, bsX, bsY, viewport);
      if(cameraType != CameraTypes.Orthogonal) 
      {
        switch(fovType) 
        {
          case FovTypes.Horizontal:
            setHorizontalFov();
            break;
          case FovTypes.Vertical:
            setVerticalFov();
            break;
        }
      } 
      else 
      {
        setOrtho();
      }        
    }
 
 
    private void SetWorldMatrix() 
    {
      Gl.glMatrixMode(Gl.GL_MODELVIEW);
      if(resetWorldMatrix) 
      {
        Gl.glLoadIdentity();
        if(cameraType != CameraTypes.Orthogonal) 
        {
          if(cameraType == CameraTypes.FirstPerson || cameraType == CameraTypes.Directional) 
          {
            Matrix m;
            m = rotation.ToMatrix();
            m.Scale = scale;
            m.Transpose();
            // Convert to OpenGL inverse
            Vector c1b = new Vector(m.m01, m.m11, m.m21);
            Vector c2b = new Vector(m.m02, m.m12, m.m22);
            m.m01=-c2b.X;
            m.m11=-c2b.Y;
            m.m21=-c2b.Z;
            m.m02=c1b.X;
            m.m12=c1b.Y;
            m.m22=c1b.Z;
            float [] f;
            f = m.ToFloatArray();
            Gl.glMultMatrixf(f);
            Gl.glTranslatef(-cameraPosition.X, -cameraPosition.Y, cameraPosition.Z);
          } 
          else 
          {
            // It's a target camera
            Glu.gluLookAt(cameraPosition.X,cameraPosition.Y,cameraPosition.Z,
              targetPosition.X,targetPosition.Y,targetPosition.Z,
              upVector.X, upVector.Y, upVector.Z);
          }
        } 
        else 
        {
          // If it's orthogonal, never cache it, it's slower
          cacheWorldMatrix = false;
          resetWorldMatrix = false;
          return;
        }
        // Caché projection matrix
        if(cacheWorldMatrix) 
        {
          float [] f = new float[16];
          Gl.glGetFloatv(Gl.GL_MODELVIEW_MATRIX,f);
          worldMatrix = new Matrix(f);
          resetWorldMatrix = false;
        }
      } 
      else 
      {
        Gl.glLoadMatrixf(worldMatrix);
      }
    }
 
    public void Set() 
    {
      bool updateFrustum = true;
      if(resetProjectionMatrix||resetWorldMatrix) 
      {
        updateFrustum = true;
      }
 
      if(orthoCoordsFromViewport)
      {
        float [] param = new float[4];
        Gl.glGetFloatv(Gl.GL_VIEWPORT, param);	
        orthoCoords.X1 = 0; 
        orthoCoords.X2 = param[2]; 
        orthoCoords.Y1 = param[3]; 
        orthoCoords.Y2 = 0; 
      }
 
      SetProjectionMatrix();
      SetWorldMatrix();
      // Update Frustum
      if(updateFrustum)
        viewFrustum.GetPlanes();
      Gl.glMatrixMode(Gl.GL_MODELVIEW);
    }
 
    public void SetPickingMatrix(int cX, int cY, int boxSizeX, int boxSizeY) 
    {
      bool updateFrustum = true;
      if(resetProjectionMatrix||resetWorldMatrix) 
      {
        updateFrustum = true;
      }
      SetPickingProjectionMatrix(cX, cY, boxSizeX, boxSizeY);
      SetWorldMatrix();
      // Update Frustum
      if(updateFrustum)
        viewFrustum.GetPlanes();
      Gl.glMatrixMode(Gl.GL_MODELVIEW);
    }
  }
}
 

Download camera.cs camera.cs - 19.6 KB