1 module prova.entities.camera;
2 
3 import prova.entities;
4 import prova.math;
5 
6 ///
7 enum SortingMethod { Z, DISTANCE }
8 ///
9 enum Projection { PERSPECTIVE, ORTHOGRAPHIC }
10 
11 ///
12 class Camera : Entity
13 {
14   ///
15   Projection projection = Projection.PERSPECTIVE;
16   ///
17   SortingMethod sortingMethod = SortingMethod.DISTANCE;
18   ///
19   bool useDepthBuffer = true;
20   /// Width and height will be set to the screen resolution when true
21   bool resolutionDependent = false;
22   ///
23   float zNear = float.min_normal;
24   ///
25   float zFar = 1000;
26   /// For orthographic projection and UI
27   float width = 1;
28   /// For orthographic projection and UI
29   float height = 1;
30   /// For perspective projection
31   float FOV = 90;
32 
33   ///
34   @property float zRange()
35   {
36     return zFar - zNear;
37   }
38 
39   ///
40   Matrix getViewMatrix()
41   {
42     Vector3 worldPosition = getWorldPosition();
43     Quaternion worldRotation = getWorldRotation();
44 
45     Matrix transform = Matrix.identity;
46     transform = transform.translate(-worldPosition);
47     transform = transform.rotate(worldRotation.getConjugate());
48     transform = transform.scale(scale);
49 
50     return transform;
51   }
52 
53   ///
54   Matrix getProjectionMatrix()
55   {
56     if(projection == Projection.ORTHOGRAPHIC)
57       return Matrix.ortho(-width/2, width/2, height/2, -height/2, zNear, zFar);
58     return Matrix.perspective(width, height, zNear, zFar, FOV);
59   }
60 
61   /**
62    * Converts world position to screen position
63    * - This is equal to getProjectionMatrix() * getViewMatrix()
64    */
65   Matrix getScreenMatrix()
66   {
67     return getProjectionMatrix() * getViewMatrix();
68   }
69 
70   /// 
71   Vector2 getScreenPosition(Vector3 worldPosition)
72   {
73     Vector4 worldPositionVec4 = worldPosition;
74     worldPositionVec4.w = 1;
75 
76     Vector4 clipSpacePosition = getScreenMatrix() * worldPositionVec4;
77 
78     return clipSpacePosition.xy / clipSpacePosition.w;
79   }
80 
81   ///
82   Matrix getUIMatrix()
83   {
84     const float left = 0;
85     const float right = width / scale.x;
86     const float top = height / scale.y;
87     const float bottom = 0;
88 
89     return Matrix.ortho(left, right, top, bottom, -1, 1);
90   }
91 
92   ///
93   bool delegate(Entity, Entity) getSortDelegate()
94   {
95     final switch(sortingMethod)
96     {
97       case SortingMethod.DISTANCE:
98         return (a, b) => distanceSort(a, b);
99       case SortingMethod.Z:
100         return (a, b) => zSort(a, b);
101     }
102   }
103 
104   private bool distanceSort(Entity entityA, Entity entityB)
105   {
106     float distanceA = position.distanceTo(entityA.position);
107     float distanceB = position.distanceTo(entityB.position);
108 
109     return distanceA < distanceB;
110   }
111 
112   private bool zSort(Entity entityA, Entity entityB)
113   {
114     return entityA.position.z < entityB.position.z;
115   }
116 }