summaryrefslogtreecommitdiff
path: root/Runtime/Camera/CameraUtil.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Camera/CameraUtil.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Camera/CameraUtil.cpp')
-rw-r--r--Runtime/Camera/CameraUtil.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/Runtime/Camera/CameraUtil.cpp b/Runtime/Camera/CameraUtil.cpp
new file mode 100644
index 0000000..1bdcd4b
--- /dev/null
+++ b/Runtime/Camera/CameraUtil.cpp
@@ -0,0 +1,452 @@
+#include "UnityPrefix.h"
+#include "CameraUtil.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Rect.h"
+#include "RenderManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+void FlipScreenRectIfNeeded( const GfxDevice& device, int screenviewcoord[4] )
+{
+ // Flip viewport rect vertically for D3D or dashboard widgets.
+ // But only do it when rendering to screen, not a texture!
+ if( (!device.UsesOpenGLTextureCoords() || device.GetInvertProjectionMatrix()) && device.GetActiveRenderTexture() == NULL )
+ {
+ int height;
+ #if GFX_SUPPORTS_D3D9
+ // On D3D, always use height of current actual render target. Others may be off, particularly in editor's game view
+ if (device.GetRenderer() == kGfxRendererD3D9 || device.GetRenderer() == kGfxRendererD3D11)
+ height = device.GetCurrentTargetHeight();
+ else
+ #endif
+ // Use screen height, not render manager's window height. In editor's GameView, the window height might be smaller.
+ height = GetScreenManager().GetHeight();
+
+ int miny = height - screenviewcoord[1];
+ int maxy = height - (screenviewcoord[3] + screenviewcoord[1]);
+ if (maxy < miny)
+ std::swap(maxy, miny);
+ miny = std::max(miny, 0);
+ screenviewcoord[1] = miny;
+ screenviewcoord[3] = maxy - miny;
+ DebugAssertIf( screenviewcoord[1] < 0 || screenviewcoord[3] < 0 );
+ }
+ #if GFX_USES_VIEWPORT_OFFSET
+ if( device.GetActiveRenderTexture() == NULL )
+ {
+ float xOffs, yOffs;
+ device.GetViewportOffset(xOffs, yOffs);
+ screenviewcoord[0] += xOffs;
+ screenviewcoord[1] += yOffs;
+ }
+ #endif
+ #if UNITY_WP8
+ ScreenOrientation const screenOrientation = GetScreenManager().GetScreenOrientation();
+ bool renderToTexture = device.GetActiveRenderTexture() != NULL;
+ bool rotated = (screenOrientation == ScreenOrientation::kLandscapeLeft) || (screenOrientation == ScreenOrientation::kLandscapeRight);
+ if (rotated && !renderToTexture)
+ {
+ float x = screenviewcoord[0];
+ float y = screenviewcoord[1];
+
+ if (screenOrientation == ScreenOrientation::kLandscapeRight)
+ {
+ float width = screenviewcoord[2];
+ float targetWidth = device.GetActiveRenderTexture() ? device.GetCurrentTargetWidth() : GetScreenManager().GetWidth();
+ screenviewcoord[0] = y;
+ screenviewcoord[1] = targetWidth - x - width;
+ }
+ else if (screenOrientation == ScreenOrientation::kLandscapeLeft)
+ {
+ float height = screenviewcoord[3];
+ float targetHeight = device.GetActiveRenderTexture() ? device.GetCurrentTargetHeight() : GetScreenManager().GetHeight();
+ screenviewcoord[0] = targetHeight - y - height;
+ screenviewcoord[1] = x;
+ }
+
+ std::swap(screenviewcoord[2], screenviewcoord[3]);
+ }
+ #endif
+}
+
+#if UNITY_WP8
+
+void RotateScreenIfNeeded(Matrix4x4f& projection)
+{
+ ScreenOrientation const screenOrientation = GetScreenManager().GetScreenOrientation();
+ if (screenOrientation == ScreenOrientation::kLandscapeLeft)
+ {
+ Vector4f const x = projection.GetRow(0);
+ Vector4f const y = projection.GetRow(1);
+ projection.SetRow(0, y);
+ projection.SetRow(1, -x);
+ }
+ else if (screenOrientation == ScreenOrientation::kLandscapeRight)
+ {
+ Vector4f const x = projection.GetRow(0);
+ Vector4f const y = projection.GetRow(1);
+ projection.SetRow(0, -y);
+ projection.SetRow(1, x);
+ }
+}
+
+static void RotatePointIfNeeded(Vector3f& point, bool unrotate)
+{
+ ScreenOrientation const screenOrientation = GetScreenManager().GetScreenOrientation();
+ if (screenOrientation == ScreenOrientation::kLandscapeLeft)
+ {
+ auto const x = point.x;
+ auto const y = point.y;
+
+ if (unrotate)
+ {
+ point.x = y;
+ point.y = -x;
+ }
+ else
+ {
+ point.x = -y;
+ point.y = x;
+ }
+ }
+ else if (screenOrientation == ScreenOrientation::kLandscapeRight)
+ {
+ auto const x = point.x;
+ auto const y = point.y;
+
+ if (unrotate)
+ {
+ point.x = -y;
+ point.y = x;
+ }
+ else
+ {
+ point.x = y;
+ point.y = -x;
+ }
+ }
+}
+
+#endif
+
+void CalcPixelMatrix (const Rectf& screenRect, Matrix4x4f &out)
+{
+ out.SetOrtho( screenRect.x, screenRect.GetRight(), screenRect.y, screenRect.GetBottom(), -1.0f, 100.0f );
+ #if UNITY_WP8
+ RotateScreenIfNeeded(out);
+ #endif
+}
+
+void ApplyTexelOffsetsToPixelMatrix( bool invertYTexelOffset, Matrix4x4f& matrix )
+{
+ float offsetX, offsetY;
+ GetHalfTexelOffsets( offsetX, offsetY );
+ if( invertYTexelOffset )
+ offsetY = -offsetY;
+
+ matrix.Get(0,3) -= offsetX * matrix.Get(0,0);
+ matrix.Get(1,3) -= offsetY * matrix.Get(1,1);
+}
+
+
+void LoadPixelMatrix( const Rectf& screenRect, GfxDevice& device, bool setMatrix, bool invertYTexelOffset )
+{
+ Matrix4x4f ortho;
+ CalcPixelMatrix( screenRect, ortho );
+ ApplyTexelOffsetsToPixelMatrix( invertYTexelOffset, ortho );
+ device.SetProjectionMatrix (ortho);
+ if( setMatrix )
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+}
+
+void GetHalfTexelOffsets( float& outx, float& outy )
+{
+ GfxDevice& device = GetGfxDevice();
+ if( device.UsesHalfTexelOffset() )
+ {
+ outx = 0.5f;
+ outy = 0.5f;
+ if( device.GetActiveRenderTexture() == NULL )
+ outy = -outy;
+ }
+ else
+ {
+ outx = outy = 0.0f;
+ }
+}
+
+void LoadFullScreenOrthoMatrix( float nearPlane, float farPlane, bool forceNoHalfTexelOffset )
+{
+ GfxDevice& device = GetGfxDevice();
+ float offsetX, offsetY;
+ if( device.UsesHalfTexelOffset() && !forceNoHalfTexelOffset)
+ {
+ // viewport of the device should always have the correct size of
+ // the render target (both when rendering to screen and to a
+ // render texture), so calc half texel offset from that.
+ int viewport[4];
+ device.GetViewport(viewport);
+ int width = viewport[2];
+ int height = viewport[3];
+ offsetX = width ? (0.5f / width) : 0.0f;
+ offsetY = height ? (0.5f / height) : 0.0f;
+ if( device.GetActiveRenderTexture() == NULL )
+ offsetY = -offsetY;
+ }
+ else
+ {
+ offsetX = offsetY = 0.0f;
+ }
+ Matrix4x4f matrix;
+ matrix.SetOrtho( offsetX, 1.0f + offsetX, offsetY, 1.0f + offsetY, nearPlane, farPlane );
+ device.SetProjectionMatrix (matrix);
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+}
+
+void SetupPixelCorrectCoordinates()
+{
+ GfxDevice& device = GetGfxDevice();
+
+ int viewcoords[4];
+ Rectf r = GetRenderManager ().GetWindowRect();
+ RectfToViewport( r, viewcoords );
+ FlipScreenRectIfNeeded( device, viewcoords );
+ device.SetViewport( viewcoords[0], viewcoords[1], viewcoords[2], viewcoords[3] );
+
+ LoadPixelMatrix( r, device, true, false );
+}
+
+void RectfToViewport( const Rectf& r, int viewPort[4] )
+{
+ // We have to take care that the viewport doesn't exceed the buffer size (case 569703).
+ // Bad rounding to integer makes D3D11 crash and burn.
+ viewPort[0] = RoundfToInt (r.x);
+ viewPort[1] = RoundfToInt (r.y);
+ viewPort[2] = RoundfToIntPos (r.GetRight ()) - viewPort[0];
+ viewPort[3] = RoundfToIntPos (r.GetBottom ()) - viewPort[1];
+}
+
+
+bool CameraProject( const Vector3f& p, const Matrix4x4f& cameraToWorld, const Matrix4x4f& worldToClip, const int viewport[4], Vector3f& outP )
+{
+ Vector3f clipPoint;
+ if( worldToClip.PerspectiveMultiplyPoint3( p, clipPoint ) )
+ {
+ Vector3f cameraPos = cameraToWorld.GetPosition();
+ Vector3f dir = p - cameraPos;
+ // The camera/projection matrices follow OpenGL convention: positive Z is towards the viewer.
+ // So negate it to get into Unity convention.
+ Vector3f forward = -cameraToWorld.GetAxisZ();
+ float dist = Dot( dir, forward );
+
+ #if UNITY_WP8
+ RotatePointIfNeeded(clipPoint, false);
+ #endif
+
+ outP.x = viewport[0] + (1.0f + clipPoint.x) * viewport[2] * 0.5f;
+ outP.y = viewport[1] + (1.0f + clipPoint.y) * viewport[3] * 0.5f;
+ //outP.z = (1.0f + clipPoint.z) * 0.5f;
+ outP.z = dist;
+
+ return true;
+ }
+
+ outP.Set( 0.0f, 0.0f, 0.0f );
+ return false;
+}
+
+bool CameraUnProject( const Vector3f& p, const Matrix4x4f& cameraToWorld, const Matrix4x4f& clipToWorld, const int viewport[4], Vector3f& outP )
+{
+ // pixels to -1..1
+ Vector3f in;
+ in.x = (p.x - viewport[0]) * 2.0f / viewport[2] - 1.0f;
+ in.y = (p.y - viewport[1]) * 2.0f / viewport[3] - 1.0f;
+ // It does not matter where the point we unproject lies in depth; so we choose 0.95, which
+ // is further than near plane and closer than far plane, for precision reasons.
+ // In a perspective camera setup (near=0.1, far=1000), a point at 0.95 projected depth is about
+ // 5 units from the camera.
+ in.z = 0.95f;
+
+ #if UNITY_WP8
+ RotatePointIfNeeded(in, true);
+ #endif
+
+ Vector3f pointOnPlane;
+ if( clipToWorld.PerspectiveMultiplyPoint3( in, pointOnPlane ) )
+ {
+ // Now we have a point on the plane perpendicular to the viewing direction. We need to return the one that is on the line
+ // towards this point, and at p.z distance along camera's viewing axis.
+ Vector3f cameraPos = cameraToWorld.GetPosition();
+ Vector3f dir = pointOnPlane - cameraPos;
+
+ // The camera/projection matrices follow OpenGL convention: positive Z is towards the viewer.
+ // So negate it to get into Unity convention.
+ Vector3f forward = -cameraToWorld.GetAxisZ();
+ float distToPlane = Dot( dir, forward );
+ if( Abs(distToPlane) >= 1.0e-6f )
+ {
+ bool isPerspective = (clipToWorld.m_Data[3] != 0.0f || clipToWorld.m_Data[7] != 0.0f || clipToWorld.m_Data[11] != 0.0f || clipToWorld.m_Data[15] != 1.0f);
+ if( isPerspective )
+ {
+ dir *= p.z / distToPlane;
+ outP = cameraPos + dir;
+ }
+ else
+ {
+ outP = pointOnPlane - forward * (distToPlane - p.z);
+ }
+ return true;
+ }
+ }
+
+ outP.Set( 0.0f, 0.0f, 0.0f );
+ return false;
+}
+
+void SetGLViewport (const Rectf& pixelRect)
+{
+ Rectf tempPixelRect (pixelRect);
+ int viewport[4];
+
+ GfxDevice& device = GetGfxDevice();
+
+#if UNITY_EDITOR
+ // Handle game view's aspect ratio dropdown, but only if we're not rendering into a render
+ // texture.
+ if( device.GetActiveRenderTexture() == NULL )
+ {
+ Rectf renderRect = GetRenderManager().GetWindowRect();
+ tempPixelRect.x += renderRect.x;
+ tempPixelRect.y += renderRect.y;
+ tempPixelRect.Clamp (renderRect);
+ }
+#endif
+
+ viewport[0] = RoundfToInt( tempPixelRect.x );
+ viewport[1] = RoundfToInt( tempPixelRect.y );
+ viewport[2] = RoundfToIntPos( tempPixelRect.Width() );
+ viewport[3] = RoundfToIntPos( tempPixelRect.Height() );
+ FlipScreenRectIfNeeded( device, viewport );
+ device.SetViewport( viewport[0], viewport[1], viewport[2], viewport[3] );
+
+}
+
+void ExtractProjectionPlanes( const Matrix4x4f& finalMatrix, Plane* outPlanes )
+{
+ float tmpVec[4];
+ float otherVec[4];
+
+ tmpVec[0] = finalMatrix.Get (3, 0);
+ tmpVec[1] = finalMatrix.Get (3, 1);
+ tmpVec[2] = finalMatrix.Get (3, 2);
+ tmpVec[3] = finalMatrix.Get (3, 3);
+
+ otherVec[0] = finalMatrix.Get (0, 0);
+ otherVec[1] = finalMatrix.Get (0, 1);
+ otherVec[2] = finalMatrix.Get (0, 2);
+ otherVec[3] = finalMatrix.Get (0, 3);
+
+ // left & right
+ outPlanes[kPlaneFrustumLeft].SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumLeft].NormalizeUnsafe();
+ outPlanes[kPlaneFrustumRight].SetABCD (-otherVec[0] + tmpVec[0], -otherVec[1] + tmpVec[1], -otherVec[2] + tmpVec[2], -otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumRight].NormalizeUnsafe();
+
+ // bottom & top
+ otherVec[0] = finalMatrix.Get (1, 0);
+ otherVec[1] = finalMatrix.Get (1, 1);
+ otherVec[2] = finalMatrix.Get (1, 2);
+ otherVec[3] = finalMatrix.Get (1, 3);
+
+ outPlanes[kPlaneFrustumBottom].SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumBottom].NormalizeUnsafe();
+ outPlanes[kPlaneFrustumTop].SetABCD (-otherVec[0] + tmpVec[0], -otherVec[1] + tmpVec[1], -otherVec[2] + tmpVec[2], -otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumTop].NormalizeUnsafe();
+
+ otherVec[0] = finalMatrix.Get (2, 0);
+ otherVec[1] = finalMatrix.Get (2, 1);
+ otherVec[2] = finalMatrix.Get (2, 2);
+ otherVec[3] = finalMatrix.Get (2, 3);
+
+ // near & far
+ outPlanes[kPlaneFrustumNear].SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumNear].NormalizeUnsafe();
+ outPlanes[kPlaneFrustumFar].SetABCD (-otherVec[0] + tmpVec[0], -otherVec[1] + tmpVec[1], -otherVec[2] + tmpVec[2], -otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumFar].NormalizeUnsafe();
+}
+
+void ExtractProjectionNearPlane( const Matrix4x4f& finalMatrix, Plane* outPlane )
+{
+ float tmpVec[4];
+ float otherVec[4];
+
+ tmpVec[0] = finalMatrix.Get (3, 0);
+ tmpVec[1] = finalMatrix.Get (3, 1);
+ tmpVec[2] = finalMatrix.Get (3, 2);
+ tmpVec[3] = finalMatrix.Get (3, 3);
+
+ otherVec[0] = finalMatrix.Get (2, 0);
+ otherVec[1] = finalMatrix.Get (2, 1);
+ otherVec[2] = finalMatrix.Get (2, 2);
+ otherVec[3] = finalMatrix.Get (2, 3);
+
+ // near
+ outPlane->SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlane->NormalizeUnsafe();
+}
+
+
+void SetClippingPlaneShaderProps()
+{
+ GfxDevice& device = GetGfxDevice();
+ BuiltinShaderParamValues& params = device.GetBuiltinParamValues();
+
+ const Matrix4x4f* viewMatrix = (const Matrix4x4f*)device.GetViewMatrix();
+ const Matrix4x4f* deviceProjMatrix = (const Matrix4x4f*)device.GetDeviceProjectionMatrix();
+ Matrix4x4f viewProj;
+ MultiplyMatrices4x4 (deviceProjMatrix, viewMatrix, &viewProj);
+ Plane planes[6];
+ ExtractProjectionPlanes (viewProj, planes);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes0, (const Vector4f&)planes[0]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes1, (const Vector4f&)planes[1]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes2, (const Vector4f&)planes[2]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes3, (const Vector4f&)planes[3]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes4, (const Vector4f&)planes[4]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes5, (const Vector4f&)planes[5]);
+}
+
+
+DeviceMVPMatricesState::DeviceMVPMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ CopyMatrix(device.GetViewMatrix(), m_View.GetPtr());
+ CopyMatrix(device.GetWorldMatrix(), m_World.GetPtr());
+ CopyMatrix(device.GetProjectionMatrix(), m_Proj.GetPtr());
+}
+
+DeviceMVPMatricesState::~DeviceMVPMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ device.SetViewMatrix(m_View.GetPtr());
+ device.SetWorldMatrix(m_World.GetPtr());
+ device.SetProjectionMatrix(m_Proj);
+ SetClippingPlaneShaderProps();
+}
+
+DeviceViewProjMatricesState::DeviceViewProjMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ CopyMatrix(device.GetViewMatrix(), m_View.GetPtr());
+ CopyMatrix(device.GetProjectionMatrix(), m_Proj.GetPtr());
+}
+
+DeviceViewProjMatricesState::~DeviceViewProjMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ device.SetViewMatrix(m_View.GetPtr());
+ device.SetProjectionMatrix(m_Proj);
+ SetClippingPlaneShaderProps();
+}