2 /***************************************************************************
4 * Copyright (c) Raphaël Ernaelsten (@RaphErnaelsten) *
5 * All Rights Reserved. *
7 * NOTICE: Aura 2 is a commercial project. *
8 * All information contained herein is, and remains the property of *
9 * Raphaël Ernaelsten. *
10 * The intellectual and technical concepts contained herein are *
11 * proprietary to Raphaël Ernaelsten and are protected by copyright laws. *
12 * Dissemination of this information or reproduction of this material *
13 * is strictly forbidden. *
15 ***************************************************************************/
22 /// Static class containing extension for Camera type
24 public static class CameraExtensions
26 #region Private Members
28 /// Clip space corners' position
30 private static readonly Vector3[] _frustumClipPos = // E_______________F
32 new Vector3(-1, 1, -1), // A // / | _/ |
33 new Vector3(1, 1, -1), // B // / | _/ | FAR
34 new Vector3(1, -1, -1), // C // / | _/ |
35 new Vector3(-1, -1, -1), // D // / H____/__________G
36 new Vector3(-1, 1, 1), // E // A____/__B __/
37 new Vector3(1, 1, 1), // F // | _/ | __/
38 new Vector3(1, -1, 1), // G // NEAR |_/ | __/
39 new Vector3(-1, -1, 1) // H // D_______C_/
43 /// The automatic spawn distance from the camera for gameObjects creation
45 private static readonly float _spawnDistanceFromCamera = 50.0f;
47 /// The automatic spawn downwards search distance if there is no surface to spawn onto at _spawnDistanceFromCamera
49 private static readonly float _spawnHeightTolerance = 25.0f;
51 /// Temporary object to avoid allocation
53 private static Vector3[] _tmpRetrievedFrustumPlaneCornersArray = new Vector3[4];
55 /// Temporary object to avoid allocation
57 private static Vector4[] _tmpNearPlaneCornersArray = new Vector4[4];
59 /// Temporary object to avoid allocation
61 private static Vector4[] _tmpFarPlaneCornersArray = new Vector4[4];
66 /// Tells if the current camera is the sceneView camera
68 public static bool IsCurrentSceneViewCamera
72 return Camera.current != null && Camera.current.IsSceneViewCamera();
77 /// Tells if the reference camera is the SceneView camera
79 /// <returns>True if the reference camera is the current SceneView camera</returns>
80 public static bool IsSceneViewCamera(this Camera camera)
83 return camera.cameraType == CameraType.SceneView;
90 /// Computes the planes corresponding to the sides of frustum (a tapered box) of the camera
92 /// <param name="nearClipPlaneDistance">The near clip distance</param>
93 /// <param name="farClipPlaneDistance">The far clip distance</param>
94 /// <returns>An array with the planes corresponding to the sides of frustum, in the following order : left, right, top, bottomm, near, far</returns>
95 public static Plane[] GetFrustumPlanes(this Camera camera, float nearClipPlaneDistance, float farClipPlaneDistance)
97 Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(camera);
98 // Overwrite near and far planes
99 frustumPlanes[4] = new Plane(camera.transform.forward, camera.transform.position + camera.transform.forward * nearClipPlaneDistance); // near
100 frustumPlanes[5] = new Plane(-camera.transform.forward, camera.transform.position + camera.transform.forward * farClipPlaneDistance); // far
102 return frustumPlanes;
106 /// Computes the size of the frustum at given distance
108 /// <param name="distance">The distance at which we want to know the size of the frustum</param>
109 /// <returns>The size of the frustum at given distance</returns>
110 public static Vector2 GetFrustumSizeAtDistance(this Camera camera, float distance)
112 float height = Mathf.Tan(camera.fieldOfView * Mathf.Deg2Rad * 0.5f) * 2.0f * distance;
113 float width = height * camera.aspect;
115 return new Vector2(width, height);
119 /// Returns the viewing eye for Stereo rendering (if not stereo or not in a render loop, returns Left)
121 /// <returns>The current eye (left if not stereo or not in a render loop)</returns>
122 public static Camera.StereoscopicEye GetStereoscopicEye(this Camera camera)
124 return (Camera.StereoscopicEye)((int)camera.stereoActiveEye % 2);
128 /// Computes the corners of the plane parallel to the camera at given distance
130 /// <param name="eye">The stereoscopic eye</param>
131 /// <param name="planeDistance">The reference distance</param>
132 /// <param name="planeCornersArray">The array to fill with the four corners of the intersecting plane</param>
133 public static void GetFrustumPlaneCorners(this Camera camera, Camera.MonoOrStereoscopicEye eye, float planeDistance, ref Vector4[] planeCornersArray)
135 if (camera.orthographic)
137 _tmpRetrievedFrustumPlaneCornersArray[0].x = 0.0f;
138 _tmpRetrievedFrustumPlaneCornersArray[0].y = 1.0f;
139 _tmpRetrievedFrustumPlaneCornersArray[0].z = planeDistance;
140 _tmpRetrievedFrustumPlaneCornersArray[1].x = 1.0f;
141 _tmpRetrievedFrustumPlaneCornersArray[1].y = 1.0f;
142 _tmpRetrievedFrustumPlaneCornersArray[1].z = planeDistance;
143 _tmpRetrievedFrustumPlaneCornersArray[2].x = 1.0f;
144 _tmpRetrievedFrustumPlaneCornersArray[2].y = 0.0f;
145 _tmpRetrievedFrustumPlaneCornersArray[2].z = planeDistance;
146 _tmpRetrievedFrustumPlaneCornersArray[3].x = 0.0f;
147 _tmpRetrievedFrustumPlaneCornersArray[3].y = 0.0f;
148 _tmpRetrievedFrustumPlaneCornersArray[3].z = planeDistance;
150 for (int i = 0; i < 4; ++i)
152 planeCornersArray[i] = camera.ViewportToWorldPoint(_tmpRetrievedFrustumPlaneCornersArray[i]);
157 camera.CalculateFrustumCorners(new Rect(0,0,1,1), planeDistance, eye, _tmpRetrievedFrustumPlaneCornersArray);
158 for (int i = 0; i < 4; ++i)
160 _tmpRetrievedFrustumPlaneCornersArray[i] = camera.transform.localToWorldMatrix.MultiplyPoint(_tmpRetrievedFrustumPlaneCornersArray[i]);
163 Vector3 tmp = _tmpRetrievedFrustumPlaneCornersArray[0];
164 planeCornersArray[0] = new Vector4(_tmpRetrievedFrustumPlaneCornersArray[1].x, _tmpRetrievedFrustumPlaneCornersArray[1].y, _tmpRetrievedFrustumPlaneCornersArray[1].z, 1.0f);
165 planeCornersArray[1] = new Vector4(_tmpRetrievedFrustumPlaneCornersArray[2].x, _tmpRetrievedFrustumPlaneCornersArray[2].y, _tmpRetrievedFrustumPlaneCornersArray[2].z, 1.0f);
166 planeCornersArray[2] = new Vector4(_tmpRetrievedFrustumPlaneCornersArray[3].x, _tmpRetrievedFrustumPlaneCornersArray[3].y, _tmpRetrievedFrustumPlaneCornersArray[3].z, 1.0f);
167 planeCornersArray[3] = new Vector4(tmp.x, tmp.y, tmp.z, 1.0f);
172 /// Computes the world space frustum's corners' position
174 /// <param name="nearClipDistance">The desired near plane</param>
175 /// <param name="farClipDistance">The desired far plane</param>
176 /// <param name="floatsArrayToFill">The floats array to fill with the the positions</param>
177 public static void GetFrustumCorners(this Camera camera, Camera.MonoOrStereoscopicEye eye, float nearClipDistance, float farClipDistance, ref float[] floatsArrayToFill)
179 camera.GetFrustumPlaneCorners(eye, nearClipDistance, ref _tmpNearPlaneCornersArray);
180 for (int i = 0; i < 4; ++i)
182 floatsArrayToFill[i * 4] = _tmpNearPlaneCornersArray[i].x;
183 floatsArrayToFill[i * 4 + 1] = _tmpNearPlaneCornersArray[i].y;
184 floatsArrayToFill[i * 4 + 2] = _tmpNearPlaneCornersArray[i].z;
185 floatsArrayToFill[i * 4 + 3] = _tmpNearPlaneCornersArray[i].w;
188 camera.GetFrustumPlaneCorners(eye, farClipDistance, ref _tmpFarPlaneCornersArray);
189 for (int i = 0; i < 4; ++i)
191 floatsArrayToFill[16 + i * 4] = _tmpFarPlaneCornersArray[i].x;
192 floatsArrayToFill[16 + i * 4 + 1] = _tmpFarPlaneCornersArray[i].y;
193 floatsArrayToFill[16 + i * 4 + 2] = _tmpFarPlaneCornersArray[i].z;
194 floatsArrayToFill[16 + i * 4 + 3] = _tmpFarPlaneCornersArray[i].w;
199 /// Computes a default spawn position in front of the camera. The position will be in this order : a) on the surface in front of the camera (if within 50m). b) if not, 50m in front of the camera, on a surface which would be within 25m under. c) if not, 50m in front of the camera.
201 /// <returns>The computed spawn position</returns>
202 public static Vector3 GetSpawnPosition(this Camera camera)
204 Vector3 spawnPosition;
206 RaycastHit hitInfo = new RaycastHit();
207 if (Physics.Raycast(camera.transform.position, camera.transform.forward, out hitInfo, _spawnDistanceFromCamera))
209 spawnPosition = hitInfo.point;
213 spawnPosition = camera.transform.position + camera.transform.forward * _spawnDistanceFromCamera;
215 if (Physics.Raycast(spawnPosition, Vector3.down, out hitInfo, _spawnHeightTolerance))
217 spawnPosition = hitInfo.point;
221 return spawnPosition;
225 /// Computes the world position of the frustum corners with custom near/far clip distances
227 /// <param name="camera">The queried camera</param>
228 /// <param name="nearClipPlaneDistance">The near clip distance</param>
229 /// <param name="farClipPlaneDistance">The far clip distance</param>
230 /// <returns>An array with the world position of the corners in the following order : nearTopLeft, nearTopRight, nearBottomRight, nearBottomLeft, farTopLeft, farTopRight, farBottomRight, farBottomLeft</returns>
231 public static Vector4[] GetViewportFrustumCornersWorldPosition(this Camera camera, float nearClipPlaneDistance, float farClipPlaneDistance)
235 camera.ViewportToWorldPoint(new Vector3(0, 1, nearClipPlaneDistance)),
236 camera.ViewportToWorldPoint(new Vector3(1, 1, nearClipPlaneDistance)),
237 camera.ViewportToWorldPoint(new Vector3(1, 0, nearClipPlaneDistance)),
238 camera.ViewportToWorldPoint(new Vector3(0, 0, nearClipPlaneDistance)),
239 camera.ViewportToWorldPoint(new Vector3(0, 1, farClipPlaneDistance)),
240 camera.ViewportToWorldPoint(new Vector3(1, 1, farClipPlaneDistance)),
241 camera.ViewportToWorldPoint(new Vector3(1, 0, farClipPlaneDistance)),
242 camera.ViewportToWorldPoint(new Vector3(0, 0, farClipPlaneDistance))
247 /// Computes the world position of the corners of the frustum
249 /// <param name="frustumClipToCameraInverseProjMatrix">The clip to camera inverse projection matrix</param>
250 /// <returns>An array containing the world position of the corners of the frustum</returns>
251 public static Vector4[] GetFrustumCornersWorldPosition(this Camera camera, Matrix4x4 frustumClipToCameraInverseProjMatrix)
253 Vector4[] frustumWorldPos = new Vector4[8];
255 for (int i = 0; i < 8; ++i)
257 frustumWorldPos[i] = frustumClipToCameraInverseProjMatrix.MultiplyPoint(_frustumClipPos[i]);
260 return frustumWorldPos;
264 /// Computes the world position of the corners of the frustum
266 /// <param name="camera">The camera</param>
267 /// <param name="nearClipPlaneDistance">The near plane</param>
268 /// <param name="farClipPlaneDistance">The far plane</param>
269 /// <returns>An array containing the world position of the corners of the frustum</returns>
270 public static Vector3[] GetFrustumCornersWorldPosition(this Camera camera, float nearClipPlaneDistance, float farClipPlaneDistance)
272 Vector3[] frustumWorldPos = new Vector3[8];
274 Vector2 near = camera.GetFrustumSizeAtDistance(nearClipPlaneDistance);
275 Vector2 far = camera.GetFrustumSizeAtDistance(farClipPlaneDistance);
277 Matrix4x4 matrix = Matrix4x4.TRS(camera.transform.position, camera.transform.rotation, Vector3.one);
279 for (int i = 0; i < 8; ++i)
281 Vector3 pos = _frustumClipPos[i];
286 pos.z = nearClipPlaneDistance;
292 pos.z = farClipPlaneDistance;
295 frustumWorldPos[i] = matrix.MultiplyPoint3x4(pos);
298 return frustumWorldPos;
302 /// Gets the projection matrix of the camera
304 /// <param name="eye">The stereoscopic eye</param>
305 /// <returns></returns>
306 public static Matrix4x4 GetProjectionMatrix(this Camera camera, Camera.MonoOrStereoscopicEye eye)
308 if (eye == Camera.MonoOrStereoscopicEye.Mono)
310 return camera.projectionMatrix;
314 return camera.GetStereoProjectionMatrix((Camera.StereoscopicEye)eye);
319 /// Returns the camera's World space to Clip space matrix
321 /// <param name="nearClipPlane">The near clip plane</param>
322 /// <param name="farClipPlane">The far clip plane</param>
323 /// <param name="matrixToFill">The allocated matrix object to write into</param>
324 public static void GetWorldToClipMatrix(this Camera cameraComponent, Camera.MonoOrStereoscopicEye eye, float nearClipPlane, float farClipPlane, ref Matrix4x4 matrixToFill)
326 float tmpNear = cameraComponent.nearClipPlane;
327 cameraComponent.nearClipPlane = nearClipPlane;
328 float tmpFar = cameraComponent.farClipPlane;
329 cameraComponent.farClipPlane = farClipPlane;
331 Matrix4x4 worldToCameraMatrix = cameraComponent.worldToCameraMatrix;
332 Matrix4x4 projectionMatrix = cameraComponent.GetProjectionMatrix(eye);
333 matrixToFill = projectionMatrix * worldToCameraMatrix; // worldToClipMatrix
335 cameraComponent.nearClipPlane = tmpNear;
336 cameraComponent.farClipPlane = tmpFar;
340 /// Returns the stereo mode of the camera
342 /// <returns>What stereo mode the camera is running on</returns>
343 public static StereoMode GetCameraStereoMode(this Camera camera)
345 if (camera.stereoEnabled)
347 if(XrHelpers.IsSinglePassStereo)
349 return StereoMode.SinglePass;
353 return StereoMode.MultiPass;
358 return StereoMode.Mono;