BITFALL/Assets/GSpawn - Level Designer/Scripts/Prefabs/PrefabPreviewFactory.cs

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