ecere/gfx/3D: Lighting fixes; Meta shader; normal mapping; environment mapping; cube...
[sdk] / ecere / src / gfx / 3D / Camera.ec
1 namespace gfx3D;
2
3 import "Display"
4
5 public enum CameraType { fixed, fixedQuaternion, attached, attachedQuaternion, lookAt, lookAtObject };
6 public enum FovDirection { widest, horizontal, vertical };
7
8 public enum ClippingPlane { left, right, top, bottom, near, far };
9
10 public class Camera
11 {
12 public:
13    // Change type to inherited types...
14    property CameraType type { set { type = value; } get { return type; } };
15    property Vector3D position { set { position = value; } get { value = position; } };
16    property Quaternion orientation { set { orientation = value; if(type == attached || type == fixed) eulerOrientation = value; } get { value = orientation; } };
17    property Euler eulerOrientation { set { eulerOrientation = value; if(type != attached && type != fixed) orientation = value; } get { value = eulerOrientation; } };
18    property Vector3D cPosition { get { value = cPosition; } };
19    property Quaternion cOrientation { get { value = cAngle; } };
20    property Degrees fov { set { fov = value; } get { return fov; } };
21    property float zMin { set { zMin = value; } get { return zMin; } };
22    property float zMax { set { zMax = value; } get { return zMax; } };
23    property Object target { set { target = value; } get { return target; } };
24    property FovDirection fovDirection { set { fovDirection = value; } get { return fovDirection; } };
25    property float aspectRatio { set { aspectRatio = value; } get { return aspectRatio; } };
26    property Size focal { get { value = { focalX, focalY }; } };
27
28    void Setup(int width, int height, Point origin)
29    {
30       if(this)
31       {
32          Quaternion quat;
33
34          float aspectRatio = this.aspectRatio;
35          float l, r, t, b;
36
37          if(!aspectRatio)
38             aspectRatio = (float)width/height;
39
40          this.width = width;
41          this.height = height;
42          if(origin != null)
43             this.origin = origin;
44          else
45          {
46             this.origin.x = width/2;
47             this.origin.y = height/2;
48          }
49
50          l = this.origin.x - 0;
51          r = this.origin.x - width;
52          t = 0 - this.origin.y;
53          b = height - this.origin.y;
54
55          if(fovDirection == horizontal || (fovDirection == widest && width * aspectRatio > height))
56          {
57             focalX = (float)((width / 2) / tan(fov/2));
58             focalY = focalX * height / width;
59             focalY *= aspectRatio;
60             fovX = fov;
61          }
62          else
63          {
64             focalY = (float)((height / 2) / tan(fov/2));
65             focalX = focalY * width / height;
66             focalX /= aspectRatio;
67             fovY = fov;
68          }
69
70          fovX = atan((width / 2)  / focalX) * 2;
71          fovY = atan((height / 2) / focalY) * 2;
72
73          fovLeft   = atan(l / focalX);
74          fovRight  = atan(r / focalX);
75          fovTop    = atan(t / focalY);
76          fovBottom = atan(b / focalY);
77
78          // Compute Clipping Planes
79          {
80             Vector3D normal, point = {0,0,0};
81
82             // --- Left ---
83             quat.Yaw(fovLeft - Pi/2);
84             quat.ToDirection(normal);
85             viewClippingPlanes[left].FromPointNormal(normal, point);
86
87             // --- Right ---
88             quat.Yaw(fovRight + Pi/2);
89             quat.ToDirection(normal);
90             viewClippingPlanes[right].FromPointNormal(normal, point);
91
92             // --- Top ---
93             quat.Pitch(fovTop + Pi/2);
94             quat.ToDirection(normal);
95             viewClippingPlanes[top].FromPointNormal(normal, point);
96
97             // --- Bottom ---
98             quat.Pitch(fovBottom - Pi/2);
99             quat.ToDirection(normal);
100             viewClippingPlanes[bottom].FromPointNormal(normal, point);
101
102             // --- Near ---
103             normal.x = 0; normal.y = 0; normal.z = 1;
104             point.z = zMin;
105             viewClippingPlanes[near].FromPointNormal(normal, point);
106
107             // --- Far ---
108             normal.x = 0; normal.y = 0; normal.z = -1;
109             point.z = zMax;
110             viewClippingPlanes[far].FromPointNormal(normal, point);
111          }
112
113          needUpdate = true;
114       }
115    }
116
117    void AdjustPosition(Vector3D position)
118    {
119       ClippingPlane c;
120       Matrix transpose = viewMatrix;
121
122       cPosition = position;
123
124       transpose.m[0][3] = cPosition.x;
125       transpose.m[1][3] = cPosition.y;
126       transpose.m[2][3] = cPosition.z;
127       inverseTranspose.Inverse(transpose);
128
129       for(c = 0; c<ClippingPlane::enumSize; c++)
130          worldClippingPlanes[c].MultMatrix(viewClippingPlanes[c], inverseTranspose);
131    }
132
133    void AdjustAngle(Quaternion angle)
134    {
135       cAngle = angle;
136
137       inverseMatrix.RotationQuaternion(angle);
138       viewMatrix.Transpose(inverseMatrix);
139
140       AdjustPosition(cPosition);
141    }
142
143    bool Update()
144    {
145       bool result = false;
146       if(this)
147       {
148          Matrix matrix;
149          Transform * target = this.target ? &this.target.transform : null;
150          Vector3D oldPosition = cPosition, newPosition;
151
152          switch(this.type)
153          {
154             case fixed:
155             case fixedQuaternion:
156             {
157                newPosition = position;
158                toAngle = orientation;
159                break;
160             }
161             case attached:
162             {
163                toAngle = { 1,0,0,0 };
164                if(target)
165                {
166                   Euler euler;
167                   Euler eulerTarget = this.target.eulerOrientation;
168
169                   euler.Add(eulerOrientation, eulerTarget);
170
171                   // Logf("yaw = %f, pitch = %f\n", eulerCamera.yaw, eulerCamera.pitch);
172
173                   toAngle = euler;
174                }
175                else
176                   toAngle = orientation;
177
178                matrix.RotationQuaternion(toAngle);
179                newPosition.MultMatrix(position, matrix);
180                if(target)
181                   newPosition.Add(newPosition, target->position);
182                break;
183             }
184             case attachedQuaternion:
185             {
186                toAngle = { 1,0,0,0 };
187                if(target)
188                {
189                   /*if(type == attached)
190                   {
191                      Euler eulerCamera = orientation, eulerTarget = target->orientation, euler;
192
193                      euler.Add(eulerCamera, eulerTarget);
194
195                      // Logf("yaw = %f, pitch = %f\n", eulerCamera.yaw, eulerCamera.pitch);
196
197                      toAngle = euler;
198                   }
199                   else if(type == attachedQuaternion)*/
200                      toAngle.Multiply(orientation, target->orientation);
201                }
202                else
203                   toAngle = orientation;
204
205                matrix.RotationQuaternion(toAngle);
206                newPosition.MultMatrix(position, matrix);
207                if(target)
208                   newPosition.Add(newPosition, target->position);
209                break;
210             }
211             case lookAt:
212             {
213                Quaternion result;
214                Vector3D direction;
215                newPosition = position;
216                if(target)
217                {
218                   direction.Subtract(target->position, position);
219                   toAngle.RotationDirection(direction);
220                }
221                else
222                {
223                   Vector3D position { 0, 0, 0 };
224                   direction.Subtract(position, this.position);
225                   toAngle.RotationDirection(direction);
226                }
227                result.Multiply(orientation, toAngle);
228                toAngle = result;
229                break;
230             }
231             case lookAtObject:
232             {
233                Object cameraObject = this.cameraObject;
234                toAngle = cameraObject.transform.orientation;
235                if(cameraObject.flags.root || !cameraObject.parent)
236                   newPosition = cameraObject.transform.position;
237                else
238                   newPosition.MultMatrix(cameraObject.transform.position, cameraObject.parent.matrix);
239                break;
240             }
241          }
242
243          if(cAngle.w != toAngle.w || cAngle.x != toAngle.x ||
244             cAngle.y != toAngle.y || cAngle.z != toAngle.z ||
245             needUpdate)
246          {
247             cPosition = newPosition;
248             if(slerpAmount && slerpPosition < 1.0)
249             {
250                Quaternion angle;
251                slerpPosition += slerpAmount;
252                slerpPosition = Min(slerpPosition, 1.0);
253                angle.Slerp(fromAngle, toAngle, slerpPosition);
254                AdjustAngle(angle);
255             }
256             else
257                AdjustAngle(toAngle);
258
259             result = true;
260             this.needUpdate = false;
261          }
262          else if(newPosition.x != oldPosition.x || newPosition.y != oldPosition.y || newPosition.z != oldPosition.z)
263          {
264             AdjustPosition(newPosition);
265             result = true;
266          }
267       }
268       return result;
269    }
270
271    bool SphereVisible(Vector3D center, float radius)
272    {
273       // TURN BACK ON
274       /*
275       if(wLeftNormal.DotProduct(center) + wLeftD < -radius)
276          return false;
277       if(wRightNormal.DotProduct(center) + wRightD < -radius)
278          return false;
279       if(wTopNormal.DotProduct(center) + wTopD < -radius)
280          return false;
281       if(wBottomNormal.DotProduct(center) + wBottomD < -radius)
282          return false;
283       if(wNearNormal.DotProduct(center) + wNearD < -radius)
284          return false;
285    /-*   if(wFarNormal.DotProduct(center) + wFarD < -radius)
286          return false;*/
287       return true;
288    }
289
290
291    bool PointsVisible(Vector3D * points, int numPoints, double threshold)
292    {
293       ClippingPlane p;
294       int c;
295       for(p = 0; p<=ClippingPlane::bottom; p+=2)
296       {
297          bool out1a = true, out2a = true;
298          bool out1b = true, out2b = true;
299          Plane * plane = &worldClippingPlanes[p];
300          for(c = 0; c<numPoints; c++)
301          {
302             double dot =
303                plane->a * points[c].x +
304                plane->b * points[c].y +
305                plane->c * points[c].z;
306             if(dot + plane->d > 0)
307             {
308                out1a = out1b = false;
309                break;
310             }
311             else if(dot + plane->d > -threshold)
312                out1a = false;
313          }
314
315          plane = &worldClippingPlanes[p+1];
316          for(c = 0; c<numPoints; c++)
317          {
318             double dot =
319                plane->a * points[c].x +
320                plane->b * points[c].y +
321                plane->c * points[c].z;
322             if(dot + plane->d > 0)
323             {
324                out2a = out2b = false;
325                break;
326             }
327             else if(dot + plane->d > -threshold)
328                out2a = false;
329          }
330
331          if((out1a && !out2b) || (out2a && !out1b))
332             return false;
333       }
334       return true;
335    }
336
337 /*
338    bool PointsVisible(Vector3D * origPoints, int numPoints, double threshold)
339    {
340       Plane * planes = worldClippingPlanes;
341       static byte goodPoints[50];
342       static Vector3D points[50];
343       static Vector3D newPoints[50];
344       bool outside = false;
345       int p;
346       int i;
347       int c = 0;
348       int n = numPoints;
349
350       for(c = 0; c<numPoints; c++)
351          points[c] = origPoints[c];
352
353       for(p = 0; p < 6; p++)
354       {
355          Plane * plane = &planes[p];
356          int i;
357          int numGoodPoints = 0;
358
359          memset(goodPoints, 0, n);
360               for(i = 0; i < n; i++)
361          {
362             double dot = plane->normal.DotProduct(points[i]);
363             double distance = dot + plane->d;
364                       if(distance > -threshold)
365             {
366                numGoodPoints++;
367                goodPoints[i] = 1;
368             }
369               }
370          if(!numGoodPoints)
371          {
372             outside = true;
373             break;
374          }
375
376          if(numGoodPoints < n)
377          {
378             // Clip the polygon
379             int newN = 0;
380             int lastGood = -1;
381             int j;
382
383             for(j = 0; j<n; )
384             {
385                if(goodPoints[j])
386                {
387                   newPoints[newN++] = points[j];
388                   lastGood = j++;
389                }
390                else
391                {
392                   Line edge;
393                   int next;
394
395                   if(lastGood == -1)
396                      for(lastGood = n-1; !goodPoints[lastGood]; lastGood--);
397
398                   edge.p0 = points[lastGood];
399                   edge.delta.Subtract(points[j], edge.p0);
400                   plane->IntersectLine(edge, newPoints[newN++]);
401
402                   for(next = j+1; next != j; next++)
403                   {
404                      if(next == n) next = 0;
405                      if(goodPoints[next])
406                      {
407                         int prev = next - 1;
408                         if(prev < 0) prev = n-1;
409
410                         edge.p0 = points[prev];
411                         edge.delta.Subtract(points[next], edge.p0);
412                         plane->IntersectLine(edge, newPoints[newN++]);
413                         break;
414                      }
415                   }
416                   if(next <= j)
417                      break;
418                   else
419                      j = next;
420                }
421             }
422             // Use the new points
423             memcpy(points, newPoints, newN * sizeof(Vector3D));
424             n = newN;
425          }
426       }
427       return !outside;
428    }
429 */
430
431    void TransformPoint(Vector3D dest, Vector3D src)
432    {
433       Vector3D vector { src.x - cPosition.x, src.y - cPosition.y, src.z - cPosition.z };
434       dest.MultMatrix(vector, viewMatrix);
435    }
436
437    void TransformNormal(Vector3D dest, Vector3D src)
438    {
439       dest.MultMatrix(src, viewMatrix);
440    }
441
442    void TransformMatrix(Matrix dest, Matrix src)
443    {
444       Matrix matrix = src;
445       matrix.m[3][0] -= cPosition.x;
446       matrix.m[3][1] -= cPosition.y;
447       matrix.m[3][2] -= cPosition.z;
448       dest.Multiply(matrix, viewMatrix);
449    }
450
451    void RotatePitch(Degrees amount, Degrees min, Degrees max)
452    {
453       if(type == fixedQuaternion)
454       {
455          orientation.RotatePitch(amount);
456       }
457       else
458       {
459          Euler euler = eulerOrientation;
460          euler.pitch += amount;
461          if(min || max)
462          {
463             euler.pitch = Min(euler.pitch, max);
464             euler.pitch = Max(euler.pitch, min);
465          }
466          eulerOrientation = euler;
467          orientation = euler;
468       }
469    }
470
471    void RotateYaw(Degrees amount, Degrees min, Degrees max)
472    {
473       if(type == fixedQuaternion)
474       {
475          orientation.RotateYaw(amount);
476       }
477       else
478       {
479          Euler euler = eulerOrientation;
480          euler.yaw += amount;
481          if(min || max)
482          {
483             euler.yaw = Min(euler.yaw, max);
484             euler.yaw = Max(euler.yaw, min);
485          }
486          eulerOrientation = euler;
487          orientation = euler;
488       }
489    }
490
491    void RotateRoll(Degrees amount, Degrees min, Degrees max)
492    {
493       if(type == fixedQuaternion)
494       {
495          orientation.RotateRoll(amount);
496       }
497       else
498       {
499          Euler euler = eulerOrientation;
500          euler.roll += amount;
501          if(min || max)
502          {
503             euler.roll = Min(euler.roll, max);
504             euler.roll = Max(euler.roll, min);
505          }
506          eulerOrientation = euler;
507          orientation = euler;
508       }
509    }
510
511    void Slerp(float amount)
512    {
513       fromAngle = cAngle;
514       slerpAmount = amount;
515       slerpPosition = 0;
516    }
517
518    void Move(Vector3D direction)
519    {
520       Matrix matrix;
521
522       switch(type)
523       {
524          case fixed:
525          {
526             Vector3D offset;
527             matrix.RotationQuaternion(orientation);
528             offset.MultMatrix(direction, matrix);
529             position.Add(position, offset);
530             break;
531          }
532          case attachedQuaternion:
533          case attached:
534          case lookAt:
535          {
536             position.Add(position, direction);
537             break;
538          }
539       }
540    }
541
542    bool Project(Vector3D vector, Vector3D point)
543    {
544       if(vector.z >= zMin)
545       {
546          //float floatZ;
547          point.x = (vector.x*focalX/vector.z);
548          point.y = (vector.y*focalY/vector.z);
549          point.z = (((zMax * zMin / -vector.z) + zMax) / (zMax - zMin));
550          //floatZ = ((((float)zMax * (float)zMin / -(float)vector.z) + (float)zMax) / ((float)zMax - (float)zMin));
551          point.x += origin.x;
552          point.y += origin.y;
553          return (point.x >= 0 && point.y >= 0 &&
554                  point.x < width && point.y < height);
555       }
556       return false;
557    }
558
559    void Unproject(Vector3D point, Vector3D vector)
560    {
561       vector.z = (zMax * zMin / (zMax - (double)point.z * (zMax-zMin)));
562       vector.y = ((point.y - origin.y) * (double)vector.z / focalY);
563       vector.x = ((point.x - origin.x) * (double)vector.z / focalX);
564    }
565
566    bool ProjectSize(Vector3D vector, Point point)
567    {
568       if(vector.z >= zMin)
569       {
570          point.x = (int)((double)vector.x*(double)focalX/(double)vector.z);
571          point.y = (int)((double)vector.y*(double)focalY/(double)vector.z);
572          return true;
573       }
574       return false;
575    }
576
577    void Untransform(Vector3D src, Vector3D result)
578    {
579       result.MultMatrix(src, inverseMatrix);
580       result.x += cPosition.x;
581       result.y += cPosition.y;
582       result.z += cPosition.z;
583    }
584
585    public void setViewMatrix(Matrix value)
586    {
587       viewMatrix = value;
588    }
589
590    public void setCPosition(Vector3D value)
591    {
592       cPosition = value;
593    }
594
595 private:
596    Camera()
597    {
598       needUpdate = true;
599       type = fixed;
600       orientation.w = 1;
601       aspectRatio = 0;
602       fov = Pi/2;
603       zMin = 5;//0.1f;
604       zMax = 10000;
605    }
606
607    CameraType type;
608    FovDirection fovDirection;
609    Object cameraObject;
610    Object target;
611    public Vector3D position;
612    Quaternion orientation;
613    Euler eulerOrientation;
614    float aspectRatio;
615    Degrees fov;
616    float zMin, zMax;
617
618    // Read only
619    Vector3D cPosition;
620    Quaternion cAngle;
621    Angle fovLeft, fovRight, fovTop, fovBottom;
622    float focalX, focalY;
623    Angle fovX, fovY;
624
625    float slerpAmount, slerpPosition;
626    Matrix inverseTranspose, inverseMatrix;
627    Quaternion fromAngle, toAngle;
628    bool needUpdate;
629    Matrix viewMatrix;
630    int width, height;
631    Point origin;
632    Plane viewClippingPlanes[ClippingPlane], worldClippingPlanes[ClippingPlane];
633 };