237 lines
9.0 KiB
C#
237 lines
9.0 KiB
C#
|
using System;
|
||
|
|
||
|
namespace UnityEngine.U2D
|
||
|
{
|
||
|
internal interface IPixelPerfectCamera
|
||
|
{
|
||
|
int assetsPPU { get; set; }
|
||
|
int refResolutionX { get; set; }
|
||
|
int refResolutionY { get; set; }
|
||
|
bool upscaleRT { get; set; }
|
||
|
bool pixelSnapping { get; set; }
|
||
|
bool cropFrameX { get; set; }
|
||
|
bool cropFrameY { get; set; }
|
||
|
bool stretchFill { get; set; }
|
||
|
}
|
||
|
|
||
|
[Serializable]
|
||
|
internal class PixelPerfectCameraInternal : ISerializationCallbackReceiver
|
||
|
{
|
||
|
// Case 1061634:
|
||
|
// In order for this class to survive hot reloading, we need to make the fields serializable.
|
||
|
// Unity can't serialize an interface object, but does properly serialize UnityEngine.Object.
|
||
|
// So we cast the reference to PixelPerfectCamera (which inherits UnityEngine.Object)
|
||
|
// before serialization happens, and restore the interface reference after deserialization.
|
||
|
[NonSerialized]
|
||
|
IPixelPerfectCamera m_Component;
|
||
|
|
||
|
PixelPerfectCamera m_SerializableComponent;
|
||
|
|
||
|
internal float originalOrthoSize;
|
||
|
internal bool hasPostProcessLayer;
|
||
|
internal bool cropFrameXAndY = false;
|
||
|
internal bool cropFrameXOrY = false;
|
||
|
internal bool useStretchFill = false;
|
||
|
internal int zoom = 1;
|
||
|
internal bool useOffscreenRT = false;
|
||
|
internal int offscreenRTWidth = 0;
|
||
|
internal int offscreenRTHeight = 0;
|
||
|
internal Rect pixelRect = Rect.zero;
|
||
|
internal float orthoSize = 1.0f;
|
||
|
internal float unitsPerPixel = 0.0f;
|
||
|
internal int cinemachineVCamZoom = 1;
|
||
|
|
||
|
internal PixelPerfectCameraInternal(IPixelPerfectCamera component)
|
||
|
{
|
||
|
m_Component = component;
|
||
|
}
|
||
|
|
||
|
public void OnBeforeSerialize()
|
||
|
{
|
||
|
m_SerializableComponent = m_Component as PixelPerfectCamera;
|
||
|
}
|
||
|
|
||
|
public void OnAfterDeserialize()
|
||
|
{
|
||
|
if (m_SerializableComponent != null)
|
||
|
m_Component = m_SerializableComponent;
|
||
|
}
|
||
|
|
||
|
internal void CalculateCameraProperties(int screenWidth, int screenHeight)
|
||
|
{
|
||
|
int assetsPPU = m_Component.assetsPPU;
|
||
|
int refResolutionX = m_Component.refResolutionX;
|
||
|
int refResolutionY = m_Component.refResolutionY;
|
||
|
bool upscaleRT = m_Component.upscaleRT;
|
||
|
bool pixelSnapping = m_Component.pixelSnapping;
|
||
|
bool cropFrameX = m_Component.cropFrameX;
|
||
|
bool cropFrameY = m_Component.cropFrameY;
|
||
|
bool stretchFill = m_Component.stretchFill;
|
||
|
|
||
|
cropFrameXAndY = cropFrameY && cropFrameX;
|
||
|
cropFrameXOrY = cropFrameY || cropFrameX;
|
||
|
useStretchFill = cropFrameXAndY && stretchFill;
|
||
|
|
||
|
// zoom level (PPU scale)
|
||
|
int verticalZoom = screenHeight / refResolutionY;
|
||
|
int horizontalZoom = screenWidth / refResolutionX;
|
||
|
zoom = Math.Max(1, Math.Min(verticalZoom, horizontalZoom));
|
||
|
|
||
|
// off-screen RT
|
||
|
useOffscreenRT = false;
|
||
|
offscreenRTWidth = 0;
|
||
|
offscreenRTHeight = 0;
|
||
|
|
||
|
if (cropFrameXOrY)
|
||
|
{
|
||
|
if (!upscaleRT)
|
||
|
{
|
||
|
if (useStretchFill)
|
||
|
{
|
||
|
useOffscreenRT = true;
|
||
|
offscreenRTWidth = zoom * refResolutionX;
|
||
|
offscreenRTHeight = zoom * refResolutionY;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
useOffscreenRT = true;
|
||
|
if (cropFrameXAndY)
|
||
|
{
|
||
|
offscreenRTWidth = refResolutionX;
|
||
|
offscreenRTHeight = refResolutionY;
|
||
|
}
|
||
|
else if (cropFrameY)
|
||
|
{
|
||
|
offscreenRTWidth = screenWidth / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
|
||
|
offscreenRTHeight = refResolutionY;
|
||
|
}
|
||
|
else // crop frame X
|
||
|
{
|
||
|
offscreenRTWidth = refResolutionX;
|
||
|
offscreenRTHeight = screenHeight / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (upscaleRT && zoom > 1)
|
||
|
{
|
||
|
useOffscreenRT = true;
|
||
|
offscreenRTWidth = screenWidth / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
|
||
|
offscreenRTHeight = screenHeight / zoom / 2 * 2;
|
||
|
}
|
||
|
|
||
|
// viewport
|
||
|
pixelRect = Rect.zero;
|
||
|
|
||
|
if (cropFrameXOrY && !upscaleRT && !useStretchFill)
|
||
|
{
|
||
|
if (cropFrameXAndY)
|
||
|
{
|
||
|
pixelRect.width = zoom * refResolutionX;
|
||
|
pixelRect.height = zoom * refResolutionY;
|
||
|
}
|
||
|
else if (cropFrameY)
|
||
|
{
|
||
|
pixelRect.width = screenWidth;
|
||
|
pixelRect.height = zoom * refResolutionY;
|
||
|
}
|
||
|
else // crop frame X
|
||
|
{
|
||
|
pixelRect.width = zoom * refResolutionX;
|
||
|
pixelRect.height = screenHeight;
|
||
|
}
|
||
|
|
||
|
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
|
||
|
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
|
||
|
}
|
||
|
else if (useOffscreenRT)
|
||
|
{
|
||
|
// When Camera.forceIntoRenderTexture is true, the size of the internal RT is determined by VP size.
|
||
|
// That's why we set the VP size to be (m_OffscreenRTWidth, m_OffscreenRTHeight) here.
|
||
|
pixelRect = new Rect(0.0f, 0.0f, offscreenRTWidth, offscreenRTHeight);
|
||
|
}
|
||
|
|
||
|
// orthographic size
|
||
|
if (cropFrameY)
|
||
|
orthoSize = (refResolutionY * 0.5f) / assetsPPU;
|
||
|
else if (cropFrameX)
|
||
|
{
|
||
|
float aspect = (pixelRect == Rect.zero) ? (float)screenWidth / screenHeight : pixelRect.width / pixelRect.height;
|
||
|
orthoSize = ((refResolutionX / aspect) * 0.5f) / assetsPPU;
|
||
|
}
|
||
|
else if (upscaleRT && zoom > 1)
|
||
|
orthoSize = (offscreenRTHeight * 0.5f) / assetsPPU;
|
||
|
else
|
||
|
{
|
||
|
float pixelHeight = (pixelRect == Rect.zero) ? screenHeight : pixelRect.height;
|
||
|
orthoSize = (pixelHeight * 0.5f) / (zoom * assetsPPU);
|
||
|
}
|
||
|
|
||
|
// Camera pixel grid spacing
|
||
|
if (upscaleRT || (!upscaleRT && pixelSnapping))
|
||
|
unitsPerPixel = 1.0f / assetsPPU;
|
||
|
else
|
||
|
unitsPerPixel = 1.0f / (zoom * assetsPPU);
|
||
|
}
|
||
|
|
||
|
internal Rect CalculatePostRenderPixelRect(float cameraAspect, int screenWidth, int screenHeight)
|
||
|
{
|
||
|
// This VP is used when the internal temp RT is blitted back to screen.
|
||
|
Rect pixelRect = new Rect();
|
||
|
|
||
|
if (useStretchFill)
|
||
|
{
|
||
|
// stretch (fit either width or height)
|
||
|
float screenAspect = (float)screenWidth / screenHeight;
|
||
|
if (screenAspect > cameraAspect)
|
||
|
{
|
||
|
pixelRect.height = screenHeight;
|
||
|
pixelRect.width = screenHeight * cameraAspect;
|
||
|
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
|
||
|
pixelRect.y = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pixelRect.width = screenWidth;
|
||
|
pixelRect.height = screenWidth / cameraAspect;
|
||
|
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
|
||
|
pixelRect.x = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// center
|
||
|
pixelRect.height = zoom * offscreenRTHeight;
|
||
|
pixelRect.width = zoom * offscreenRTWidth;
|
||
|
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
|
||
|
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
|
||
|
}
|
||
|
|
||
|
return pixelRect;
|
||
|
}
|
||
|
|
||
|
// Find a pixel-perfect orthographic size as close to targetOrthoSize as possible.
|
||
|
internal float CorrectCinemachineOrthoSize(float targetOrthoSize)
|
||
|
{
|
||
|
float correctedOrthoSize;
|
||
|
|
||
|
if (m_Component.upscaleRT)
|
||
|
{
|
||
|
cinemachineVCamZoom = Math.Max(1, Mathf.RoundToInt(orthoSize / targetOrthoSize));
|
||
|
correctedOrthoSize = orthoSize / cinemachineVCamZoom;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cinemachineVCamZoom = Math.Max(1, Mathf.RoundToInt(zoom * orthoSize / targetOrthoSize));
|
||
|
correctedOrthoSize = zoom * orthoSize / cinemachineVCamZoom;
|
||
|
}
|
||
|
|
||
|
// In this case the actual zoom level is cinemachineVCamZoom instead of zoom.
|
||
|
if (!m_Component.upscaleRT && !m_Component.pixelSnapping)
|
||
|
unitsPerPixel = 1.0f / (cinemachineVCamZoom * m_Component.assetsPPU);
|
||
|
|
||
|
return correctedOrthoSize;
|
||
|
}
|
||
|
}
|
||
|
}
|