//
// Copyright (c) 2017 Anthony Marmont. All rights reserved.
// Licensed for use under the Unity Asset Store EULA. See https://unity3d.com/legal/as_terms for full license information.
//
#if UNITY_EDITOR
#if UNITY_2017_1_OR_NEWER
#define USE_PREVIEW_SCENE
#endif
#pragma warning disable
using AssetIcons.Editors.Internal;
using AssetIcons.Editors.Internal.Product;
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace AssetIcons.Editors
{
///
/// Used by AssetIcons to render Prefabs to a .
///
///
public static class AssetIconsRenderer
{
private const int PREVIEW_LAYER = 22;
private static readonly List prefabRenderers = new List(64);
private static Scene renderScene;
private static Camera renderCamera;
private static float minX;
private static float maxX;
private static float minY;
private static float maxY;
private static Plane projectionPlaneHorizontal;
private static Plane projectionPlaneVertical;
private static Camera RenderCamera
{
get
{
if (renderCamera == null)
{
#if USE_PREVIEW_SCENE
renderScene = EditorSceneManager.NewPreviewScene();
#endif
renderCamera = EditorUtility.CreateGameObjectWithHideFlags("AssetIcons Camera",
HideFlags.HideAndDontSave).AddComponent();
renderCamera.enabled = false;
renderCamera.cullingMask = 1 << PREVIEW_LAYER;
renderCamera.nearClipPlane = 0.01f;
#if USE_PREVIEW_SCENE
renderCamera.scene = renderScene;
#endif
renderCamera.cameraType = CameraType.Preview;
}
return renderCamera;
}
}
static AssetIconsRenderer()
{
#if UNITY_5_5_OR_NEWER
var light0 = CreateLight("AssetIcons Light 0");
light0.color = new Color(1.0f, 0.95f, 0.9f);
light0.transform.rotation = Quaternion.Euler(77, 180, 0);
light0.intensity = 1.15f;
var light1 = CreateLight("AssetIcons Light 1");
light1.color = new Color(.4f, .4f, .45f, 0f) * .7f;
light1.transform.rotation = Quaternion.Euler(340, 218, 177);
light1.intensity = 1;
#endif
}
private static Light CreateLight(string name)
{
var lightGameObject = EditorUtility.CreateGameObjectWithHideFlags(name,
HideFlags.HideAndDontSave, typeof(Light));
#if USE_PREVIEW_SCENE
if (RenderCamera.scene.IsValid())
{
SceneManager.MoveGameObjectToScene(lightGameObject, RenderCamera.scene);
}
#endif
var light = lightGameObject.GetComponent();
light.type = LightType.Directional;
light.intensity = 1.0f;
light.enabled = true;
light.cullingMask = 1 << PREVIEW_LAYER;
return light;
}
///
/// Creates a preview of a model.
///
/// The object to render a preview of.
/// The style to render the model with.
/// The width (in pixels) of the rendered .
/// The height (in pixels) of the rendered .
///
/// A rendered preview .
///
public static Texture2D RenderModel(GameObject model, AssetIconsCameraSetup camera, int width = 64, int height = 64)
{
if (model == null || model.Equals(null))
{
return null;
}
GameObject previewObject;
#if USE_PREVIEW_SCENE
if (!RenderCamera.scene.IsValid())
{
previewObject = (GameObject)PrefabUtility.InstantiatePrefab(model);
}
else
{
previewObject = (GameObject)PrefabUtility.InstantiatePrefab(model, RenderCamera.scene);
}
#else
previewObject = (GameObject)PrefabUtility.InstantiatePrefab(model);
#endif
previewObject.gameObject.hideFlags = HideFlags.HideAndDontSave;
SetLayerRecursively(previewObject.transform);
previewObject.SetActive(true);
Texture2D result = null;
camera.ApplyToCamera(RenderCamera);
try
{
var previewDir = previewObject.transform.rotation * camera.PreviewDirection.normalized;
prefabRenderers.Clear();
previewObject.GetComponentsInChildren(prefabRenderers);
var previewBounds = new Bounds();
bool anyRenderers = false;
for (int i = 0; i < prefabRenderers.Count; i++)
{
var renderer = prefabRenderers[i];
if (!renderer.enabled)
{
continue;
}
if (!anyRenderers)
{
previewBounds = renderer.bounds;
anyRenderers = true;
}
else
{
previewBounds.Encapsulate(renderer.bounds);
}
}
if (!anyRenderers)
{
return null;
}
var boundsCenter = previewBounds.center;
var boundsExtents = previewBounds.extents;
var boundsSize = 2f * boundsExtents;
float aspect = (float)width / height;
RenderCamera.aspect = aspect;
RenderCamera.transform.rotation = Quaternion.LookRotation(previewDir, previewObject.transform.up);
float distance;
if (camera.Orthographic)
{
RenderCamera.transform.position = boundsCenter;
minX = minY = Mathf.Infinity;
maxX = maxY = Mathf.NegativeInfinity;
var point = boundsCenter + boundsExtents;
ProjectBoundingBoxMinMax(point);
point.x -= boundsSize.x;
ProjectBoundingBoxMinMax(point);
point.y -= boundsSize.y;
ProjectBoundingBoxMinMax(point);
point.x += boundsSize.x;
ProjectBoundingBoxMinMax(point);
point.z -= boundsSize.z;
ProjectBoundingBoxMinMax(point);
point.x -= boundsSize.x;
ProjectBoundingBoxMinMax(point);
point.y += boundsSize.y;
ProjectBoundingBoxMinMax(point);
point.x += boundsSize.x;
ProjectBoundingBoxMinMax(point);
distance = boundsExtents.magnitude + 1f;
RenderCamera.orthographicSize = (1f + camera.Padding * 2f) * Mathf.Max(maxY - minY, (maxX - minX) / aspect) * 0.5f;
}
else
{
projectionPlaneHorizontal = new Plane(RenderCamera.transform.up, boundsCenter);
projectionPlaneVertical = new Plane(RenderCamera.transform.right, boundsCenter);
float maxDistance = Mathf.NegativeInfinity;
var point = boundsCenter + boundsExtents;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
point.x -= boundsSize.x;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
point.y -= boundsSize.y;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
point.x += boundsSize.x;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
point.z -= boundsSize.z;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
point.x -= boundsSize.x;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
point.y += boundsSize.y;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
point.x += boundsSize.x;
maxDistance = RecalculateMaxDistance(maxDistance, point, boundsCenter, aspect, camera);
distance = (1f + camera.Padding * 2f) * Mathf.Sqrt(maxDistance);
}
RenderCamera.transform.position = boundsCenter - previewDir * distance;
RenderCamera.farClipPlane = distance * 4f;
var temp = RenderTexture.active;
var renderTex = RenderTexture.GetTemporary(width, height, 16, RenderTextureFormat.ARGB32);
RenderTexture.active = renderTex;
if (camera.TransparentBackground)
{
GL.Clear(false, true, camera.BackgroundColor);
}
RenderCamera.targetTexture = renderTex;
RenderCamera.Render();
RenderCamera.targetTexture = null;
var textureFormat = camera.TransparentBackground ? TextureFormat.ARGB32 : TextureFormat.RGB24;
result = new Texture2D(width, height, textureFormat, false)
{
wrapMode = TextureWrapMode.Clamp,
filterMode = FilterMode.Point
};
result.ReadPixels(new Rect(0, 0, width, height), 0, 0, false);
result.Apply(false, true);
RenderTexture.active = temp;
RenderTexture.ReleaseTemporary(renderTex);
}
catch (Exception exception)
{
Debug.LogException(exception);
}
finally
{
try
{
UnityEngine.Object.DestroyImmediate(previewObject);
}
catch (Exception destroyException)
{
previewObject.SetActive(false);
AssetIconsLogger.LogException(destroyException);
}
}
return result;
}
private static float RecalculateMaxDistance(float maxDistance, Vector3 point, Vector3 boundsCenter, float aspect, AssetIconsCameraSetup style)
{
var intersectionPoint = ClosestPointOnPlane(projectionPlaneHorizontal, point);
float horizontalDistance = projectionPlaneHorizontal.GetDistanceToPoint(point);
float verticalDistance = projectionPlaneVertical.GetDistanceToPoint(point);
float halfFrustumHeight = Mathf.Max(verticalDistance, horizontalDistance / aspect);
float distance = halfFrustumHeight / Mathf.Tan(RenderCamera.fieldOfView * 0.5f * Mathf.Deg2Rad);
float distanceToCenter = (intersectionPoint - style.PreviewDirection.normalized * distance - boundsCenter).sqrMagnitude;
if (distanceToCenter > maxDistance)
{
maxDistance = distanceToCenter;
}
return maxDistance;
}
private static Vector3 ClosestPointOnPlane(Plane plane, Vector3 point)
{
var pointToPlaneDistance = Vector3.Dot(plane.normal, point) + plane.distance;
return point - (plane.normal * pointToPlaneDistance);
}
private static void ProjectBoundingBoxMinMax(Vector3 point)
{
var localPoint = RenderCamera.transform.InverseTransformPoint(point);
if (localPoint.x < minX)
{
minX = localPoint.x;
}
if (localPoint.x > maxX)
{
maxX = localPoint.x;
}
if (localPoint.y < minY)
{
minY = localPoint.y;
}
if (localPoint.y > maxY)
{
maxY = localPoint.y;
}
}
private static void SetLayerRecursively(Transform obj)
{
obj.gameObject.layer = PREVIEW_LAYER;
for (int i = 0; i < obj.childCount; i++)
{
SetLayerRecursively(obj.GetChild(i));
}
}
}
}
#pragma warning restore
#endif