266 lines
13 KiB
C#
266 lines
13 KiB
C#
#if UNITY_EDITOR
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
|
|
namespace GSpawn
|
|
{
|
|
public class PrefabPreviewFactory
|
|
{
|
|
public struct PreviewConfig
|
|
{
|
|
public float yaw;
|
|
public float pitch;
|
|
|
|
public static readonly PreviewConfig defaultConfig = new PreviewConfig() { yaw = 0.0f, pitch = 0.0f };
|
|
}
|
|
|
|
private PrefabPreviewScene _previewScene = new PrefabPreviewScene();
|
|
private ObjectBounds.QueryConfig _boundsQConfig = new ObjectBounds.QueryConfig();
|
|
private RenderTexture _renderTexture;
|
|
|
|
public static int previewSize { get { return 128; } }
|
|
public static PrefabPreviewFactory instance { get { return GSpawn.active.prefabPreviewFactory; } }
|
|
|
|
public PrefabPreviewFactory()
|
|
{
|
|
_boundsQConfig.objectTypes = GameObjectType.All & (~(GameObjectType.Light | GameObjectType.ParticleSystem | GameObjectType.Empty | GameObjectType.Camera));
|
|
_boundsQConfig.volumelessSize = Vector3Ex.create(0.0f);
|
|
}
|
|
|
|
public void initialize()
|
|
{
|
|
_previewScene.initialize();
|
|
createRenderTexture();
|
|
}
|
|
|
|
public void cleanup()
|
|
{
|
|
destroyRenderTexture();
|
|
|
|
if (_previewScene != null)
|
|
_previewScene.cleanup();
|
|
}
|
|
|
|
public Texture2D createPreviewTexture(PrefabData prefabData)
|
|
{
|
|
bool isLinear = QualitySettings.activeColorSpace != ColorSpace.Linear;
|
|
if (prefabData.hasVolume) return new Texture2D(previewSize, previewSize, TextureFormat.ARGB32, false, isLinear);
|
|
else
|
|
{
|
|
Texture2D previewIcon = getVolumlessPreviewIcon(prefabData);
|
|
return new Texture2D(previewIcon.width, previewIcon.height, previewIcon.format, false, isLinear);
|
|
}
|
|
}
|
|
|
|
private static List<GameObject> _lodObjects = new List<GameObject>();
|
|
private static List<LODGroup> _lodGroups = new List<LODGroup>();
|
|
public void renderPreview(Texture2D previewTexture, GameObject prefabAsset, PrefabData prefabData, PreviewConfig previewConfig)
|
|
{
|
|
if (!prefabData.hasVolume)
|
|
{
|
|
Graphics.CopyTexture(getVolumlessPreviewIcon(prefabData), 0, 0, previewTexture, 0, 0);
|
|
return;
|
|
}
|
|
|
|
if (prefabData.hasMeshes || prefabData.hasSprites || prefabData.hasTerrains)
|
|
{
|
|
Transform camTransform = _previewScene.camera.transform;
|
|
RenderTexture oldRenderTexture = RenderTexture.active;
|
|
RenderTexture.active = _previewScene.camera.targetTexture;
|
|
|
|
camTransform.rotation = Quaternion.identity;
|
|
if (prefabData.hasMeshes || prefabData.hasTerrains)
|
|
camTransform.rotation = Quaternion.AngleAxis(-135.0f, Vector3.up) * Quaternion.AngleAxis(22.0f, Vector3.right);
|
|
|
|
GameObject previewObject = _previewScene.instantiatePrefab(prefabAsset);
|
|
Transform previewObjectTransform = previewObject.transform;
|
|
previewObjectTransform.position = Vector3.zero;
|
|
previewObjectTransform.rotation = Quaternion.AngleAxis(previewConfig.pitch, camTransform.right) * Quaternion.AngleAxis(previewConfig.yaw, Vector3.up);
|
|
previewObjectTransform.localScale = prefabAsset.transform.lossyScale;
|
|
|
|
// Note: For some reason, prefabs that use LOD groups, can sometimes disappear when
|
|
// rendering their previews (e.g. when pressing the reset preview button in UI).
|
|
// Need to handle prefabs with LOD groups differently.
|
|
previewObject.getLODGroupsInHierarchy(_lodGroups);
|
|
if (_lodGroups.Count != 0)
|
|
{
|
|
// Loop through each LOD group
|
|
foreach (var lodGroup in _lodGroups)
|
|
{
|
|
// Must also disable the lod group
|
|
lodGroup.enabled = false;
|
|
|
|
// Disable all renderers which are not part of LOD0
|
|
var lods = lodGroup.GetLODs();
|
|
int numLODs = lods.Length;
|
|
if (numLODs <= 1) continue;
|
|
|
|
for (int i = 1; i < numLODs; ++i)
|
|
{
|
|
var renderers = lods[i].renderers;
|
|
foreach (var r in renderers)
|
|
{
|
|
if (r != null) r.enabled = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Old code which handles LODs but can still fail in some situations.
|
|
var lodGroup = previewObject.getLODGroup();
|
|
if (lodGroup != null)
|
|
{
|
|
previewObject.getLODObjects(0, _lodObjects);
|
|
|
|
int numChildren = previewObjectTransform.childCount;
|
|
lodGroup.enabled = false;
|
|
for (int i = 0; i < numChildren; ++i)
|
|
{
|
|
GameObject childObject = previewObjectTransform.GetChild(i).gameObject;
|
|
if (_lodObjects.Contains(childObject)) continue;
|
|
|
|
// Note: Only disable mesh and skinned mesh renderers.
|
|
if (previewObject.getMeshRenderer() != null || previewObject.getSkinnedMeshRenderer() != null)
|
|
childObject.SetActive(false);
|
|
}
|
|
}*/
|
|
|
|
var oldAmbMode = RenderSettings.ambientMode;
|
|
var oldAmbLight = RenderSettings.ambientLight;
|
|
//var oldSkyboxMaterial = RenderSettings.skybox;
|
|
var oldFogEnabled = RenderSettings.fog;
|
|
RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat;
|
|
RenderSettings.ambientLight = new Color(0.1f, 0.1f, 0.1f, 0.0f);
|
|
RenderSettings.fog = false;
|
|
|
|
// Note: Currently disabled as it seems that when restoring it, the settings are not applied
|
|
// (needs clicking on the field in the LightingWindow). Therefore scenes that use a skybox
|
|
// will get a bit darker after generating prefab previews.
|
|
//RenderSettings.skybox = null; // Set skybox material to null. Otherwise, the previews appear washed out.
|
|
|
|
OBB previewOBB = ObjectBounds.calcHierarchyWorldOBB(previewObject, _boundsQConfig);
|
|
Sphere previewSphere = new Sphere(previewOBB);
|
|
float offsetFromCamera = _previewScene.camera.calcFrustumDistance(previewSphere.radius * 2.0f);
|
|
camTransform.position = previewSphere.center - camTransform.forward * (offsetFromCamera + _previewScene.camera.nearClipPlane + 0.1f);
|
|
|
|
_previewScene.mainLight.transform.forward = camTransform.forward;
|
|
_previewScene.mainLight.transform.rotation *= Quaternion.AngleAxis(-45.0f, _previewScene.mainLight.transform.right);
|
|
|
|
bool mightBeDecal = prefabData.hasMeshes && previewOBB.size.anyZero(1e-5f);
|
|
if (!mightBeDecal)
|
|
{
|
|
// Note: When rendering trees, we need to set the alpha of the background to 1.0 and
|
|
// set it back to 0 as a post process.
|
|
bool isTree = previewObject.hierarchyHasTrees(false, false);
|
|
if (isTree) _previewScene.camera.backgroundColor = Color.black;
|
|
else _previewScene.camera.backgroundColor = Color.black.createNewAlpha(0.0f);
|
|
|
|
#if GSPAWN_HDRP
|
|
if (_previewScene.hdCameraData != null)
|
|
_previewScene.hdCameraData.backgroundColorHDR = _previewScene.camera.backgroundColor;
|
|
#endif
|
|
|
|
_previewScene.camera.clearFlags = CameraClearFlags.SolidColor;
|
|
_previewScene.camera.Render();
|
|
|
|
if (!isTree)
|
|
{
|
|
previewTexture.ReadPixels(new Rect(0, 0, previewSize, previewSize), 0, 0);
|
|
previewTexture.Apply();
|
|
}
|
|
else
|
|
{
|
|
previewTexture.ReadPixels(new Rect(0, 0, previewSize, previewSize), 0, 0);
|
|
Vector4 opaqueBkColor = Color.black;
|
|
Color[] pixels = previewTexture.GetPixels();
|
|
for (int row = 0; row < previewTexture.height; ++row)
|
|
{
|
|
for (int col = 0; col < previewTexture.width; ++col)
|
|
{
|
|
int pixelIndex = row * previewTexture.width + col;
|
|
Vector4 colorVec = pixels[pixelIndex];
|
|
|
|
float distance = (colorVec - opaqueBkColor).magnitude;
|
|
if (distance < 1e-5f) pixels[pixelIndex] = pixels[pixelIndex].createNewAlpha(0.0f);
|
|
}
|
|
}
|
|
|
|
previewTexture.SetPixels(pixels);
|
|
previewTexture.Apply();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Color decalBkColor = ColorEx.create(82, 82, 82, 255);
|
|
_previewScene.camera.backgroundColor = decalBkColor;
|
|
_previewScene.camera.Render();
|
|
|
|
#if GSPAWN_HDRP
|
|
if (_previewScene.hdCameraData != null)
|
|
_previewScene.hdCameraData.backgroundColorHDR = _previewScene.camera.backgroundColor;
|
|
#endif
|
|
|
|
previewTexture.ReadPixels(new Rect(0, 0, previewSize, previewSize), 0, 0);
|
|
|
|
Vector4 decalBkColorVec = decalBkColor;
|
|
Color[] pixels = previewTexture.GetPixels();
|
|
for (int row = 0; row < previewTexture.height; ++row)
|
|
{
|
|
for (int col = 0; col < previewTexture.width; ++col)
|
|
{
|
|
int pixelIndex = row * previewTexture.width + col;
|
|
Vector4 colorVec = pixels[pixelIndex];
|
|
|
|
float distance = (colorVec - decalBkColorVec).magnitude;
|
|
if (distance < 1e-5f) pixels[pixelIndex] = pixels[pixelIndex].createNewAlpha(0.0f);
|
|
}
|
|
}
|
|
|
|
previewTexture.SetPixels(pixels);
|
|
previewTexture.Apply();
|
|
}
|
|
|
|
GameObject.DestroyImmediate(previewObject);
|
|
RenderTexture.active = oldRenderTexture;
|
|
RenderSettings.ambientMode = oldAmbMode;
|
|
RenderSettings.ambientLight = oldAmbLight;
|
|
RenderSettings.fog = oldFogEnabled;
|
|
//RenderSettings.skybox = oldSkyboxMaterial;
|
|
}
|
|
}
|
|
|
|
private Texture2D getVolumlessPreviewIcon(PrefabData prefabData)
|
|
{
|
|
if (prefabData.hasLights) return TexturePool.instance.lightGizmo;
|
|
if (prefabData.hasParticleSystems) return TexturePool.instance.particleSystemGizmo;
|
|
if (prefabData.hasCameras) return TexturePool.instance.cameraGizmo;
|
|
return TexturePool.instance.questionMark;
|
|
}
|
|
|
|
private void createRenderTexture()
|
|
{
|
|
destroyRenderTexture();
|
|
if (_previewScene.camera == null) return;
|
|
|
|
RenderTextureFormat textureFormat = _previewScene.camera.allowHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
|
|
if (PlayerSettings.colorSpace == ColorSpace.Gamma) _renderTexture = new RenderTexture(previewSize, previewSize, 32, textureFormat, RenderTextureReadWrite.Default);
|
|
else _renderTexture = new RenderTexture(previewSize, previewSize, 32, textureFormat, RenderTextureReadWrite.Default);
|
|
|
|
_renderTexture.hideFlags = HideFlags.HideAndDontSave;
|
|
_previewScene.camera.targetTexture = _renderTexture;
|
|
}
|
|
|
|
private void destroyRenderTexture()
|
|
{
|
|
if (_renderTexture != null)
|
|
{
|
|
if (_previewScene != null) _previewScene.camera.targetTexture = null;
|
|
|
|
RenderTexture.DestroyImmediate(_renderTexture);
|
|
_renderTexture = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif |