Files
ZHGD_Web/Assets/Art/Art Plugins/GPUInstancer/Scripts/Editor/GPUInstancerPreviewDrawer.cs
2025-07-13 23:16:20 +08:00

378 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
namespace GPUInstancer
{
public class GPUInstancerPreviewCache
{
private readonly Dictionary<GPUInstancerPrototype, Texture2D> _previewCache = new Dictionary<GPUInstancerPrototype, Texture2D>();
public void AddPreview(GPUInstancerPrototype prototype, Texture2D preview)
{
if (_previewCache.ContainsKey(prototype))
_previewCache[prototype] = preview;
else
_previewCache.Add(prototype, preview);
}
public void RemovePreview(GPUInstancerPrototype prototype)
{
if (_previewCache.ContainsKey(prototype))
_previewCache.Remove(prototype);
}
public Texture2D GetPreview(GPUInstancerPrototype prototype)
{
if (_previewCache.ContainsKey(prototype))
return _previewCache[prototype];
return null;
}
public void ClearEmptyPreviews()
{
GPUInstancerPrototype[] prototypes = new GPUInstancerPrototype[_previewCache.Count];
_previewCache.Keys.CopyTo(prototypes, 0);
foreach (GPUInstancerPrototype prototype in prototypes)
{
if (!_previewCache[prototype])
_previewCache.Remove(prototype);
}
}
public void ClearPreviews()
{
foreach (GPUInstancerPrototype prototype in _previewCache.Keys)
{
if (_previewCache[prototype])
Object.DestroyImmediate(_previewCache[prototype]);
}
_previewCache.Clear();
}
}
public class GPUInstancerPreviewDrawer
{
private Texture2D _defaultBackgroundTexture;
private Texture2D _additionalTexture;
#if UNITY_PRO_LICENSE
private Color _defaultColor = new Color(0.45f, 0.45f, 0.45f, 1.0f);
#else
private Color _defaultColor = new Color(0.7f, 0.7f, 0.7f, 1.0f);
#endif
private readonly Color _colorMultiplier = new Color(0.5f, 0.5f, 0.5f, 1.0f);
private Camera _camera;
private readonly List<GameObject> _gameObjects = new List<GameObject>();
private Light[] lights = new Light[2];
private readonly int _sampleLayer = 31;
public bool isVertexBased = true;
public GPUInstancerPreviewDrawer(Texture2D defaultBackgroundTexture)
{
_defaultBackgroundTexture = defaultBackgroundTexture;
InitializeCameraAndLights();
}
public void InitializeCameraAndLights()
{
var camGO = EditorUtility.CreateGameObjectWithHideFlags("Preview Scene Camera", HideFlags.HideAndDontSave, typeof(Camera));
AddGameObject(camGO);
camGO.transform.rotation = Quaternion.Euler(30, -135, 0);
_camera = camGO.GetComponent<Camera>();
_camera.cameraType = CameraType.Preview;
_camera.enabled = false;
_camera.clearFlags = CameraClearFlags.Depth;
_camera.fieldOfView = 15;
_camera.farClipPlane = 100;
_camera.nearClipPlane = -100;
_camera.renderingPath = RenderingPath.Forward;
_camera.useOcclusionCulling = false;
_camera.orthographic = true;
_camera.backgroundColor = Color.clear;
_camera.cullingMask = 1 << _sampleLayer;
_camera.allowHDR = false;
var l0 = CreateLight();
AddGameObject(l0);
lights[0] = l0.GetComponent<Light>();
var l1 = CreateLight();
AddGameObject(l1);
lights[1] = l1.GetComponent<Light>();
foreach (Light l in lights)
{
l.cullingMask = 1 << _sampleLayer;
}
lights[0].color = new Color(1f, 0.9568627f, 0.8392157f, 0f);
lights[0].transform.rotation = Quaternion.Euler(30, -135, 0);
lights[0].intensity = 1f;
lights[1].color = new Color(.4f, .4f, .45f, 0f) * .7f;
lights[1].transform.rotation = Quaternion.identity;
lights[1].intensity = 0.5f;
if (QualitySettings.activeColorSpace == ColorSpace.Linear)
{
_defaultColor = _defaultColor.linear;
lights[0].color = lights[0].color.linear;
lights[1].color = lights[1].color.linear;
}
}
public void AddGameObject(GameObject go)
{
go.layer = _sampleLayer;
if (_gameObjects.Contains(go))
return;
_gameObjects.Add(go);
}
public void SetAdditionalTexture(Texture2D additionalTexture)
{
_additionalTexture = additionalTexture;
}
public void Cleanup()
{
foreach (var go in _gameObjects)
Object.DestroyImmediate(go);
_gameObjects.Clear();
lights = null;
}
public Texture2D GetPreviewForGameObject(GameObject gameObject, Rect prevRect, Color backgroundColor, GPUInstancerRuntimeData runtimeData = null)
{
#if UNITY_2022_2_OR_NEWER
if (GPUInstancerConstants.gpuiSettings.isURP && gameObject != null) return AssetPreview.GetAssetPreview(gameObject);
#endif
if (!_camera || lights == null)
{
Cleanup();
InitializeCameraAndLights();
}
#region Initialize
float scaleFac = GetScaleFactor(prevRect.width, prevRect.height);
int rtWidth = (int)(prevRect.width * scaleFac);
int rtHeight = (int)(prevRect.height * scaleFac);
RenderTexture _renderTexture = RenderTexture.GetTemporary(rtWidth, rtHeight, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default);
_renderTexture.Create();
_camera.targetTexture = _renderTexture;
RenderTexture previousRenderTexture = RenderTexture.active;
RenderTexture.active = _renderTexture;
GL.Clear(true, true, Color.clear);
foreach (var light in lights)
light.enabled = true;
if (QualitySettings.activeColorSpace == ColorSpace.Linear)
{
if (backgroundColor != Color.clear)
backgroundColor = backgroundColor.linear;
}
if (GPUInstancerConstants.gpuiSettings.isHDRP && gameObject != null)
{
_camera.backgroundColor = backgroundColor == Color.clear ? _defaultColor.gamma : backgroundColor.gamma;
}
else if (_defaultBackgroundTexture != null)
{
GL.PushMatrix();
GL.LoadOrtho();
GL.LoadPixelMatrix(0, _renderTexture.width, _renderTexture.height, 0);
Graphics.DrawTexture(
new Rect(0, 0, _renderTexture.width, _renderTexture.height),
_defaultBackgroundTexture,
new Rect(0, 0, 1, 1),
1, 1, 1, 1, backgroundColor == Color.clear ? _defaultColor * _colorMultiplier : backgroundColor * _colorMultiplier);
GL.PopMatrix();
}
if (_additionalTexture != null)
{
GL.PushMatrix();
GL.LoadOrtho();
GL.LoadPixelMatrix(0, _renderTexture.width - 2, _renderTexture.height - 2, 0);
Graphics.DrawTexture(
new Rect(1, 1, _renderTexture.width - 1, _renderTexture.height - 1),
_additionalTexture,
new Rect(0, 0, 1, 1),
1, 1, 1, 1);
GL.PopMatrix();
}
#endregion Initialize
if (gameObject != null)
{
Renderer[] renderers;
if (gameObject.GetComponent<LODGroup>() != null)
{
renderers = gameObject.GetComponent<LODGroup>().GetLODs()[0].renderers;
}
else
{
renderers = gameObject.GetComponentsInChildren<Renderer>();
}
Bounds bounds = new Bounds();
bool isBoundsInitialized = false;
foreach (Renderer renderer in renderers)
{
if (renderer == null)
continue;
Mesh mesh = null;
if (renderer.GetComponent<MeshFilter>())
{
mesh = renderer.GetComponent<MeshFilter>().sharedMesh;
}
else if (renderer is SkinnedMeshRenderer)
{
mesh = ((SkinnedMeshRenderer)renderer).sharedMesh;
}
if (mesh != null)
{
if (isVertexBased)
{
Vector3[] verts = mesh.vertices;
for (var v = 0; v < verts.Length; v++)
{
if (!isBoundsInitialized)
{
isBoundsInitialized = true;
bounds = new Bounds(renderer.transform.localToWorldMatrix.MultiplyPoint3x4(verts[v]), Vector3.zero);
}
else
bounds.Encapsulate(renderer.transform.localToWorldMatrix.MultiplyPoint3x4(verts[v]));
}
}
else
{
Bounds rendererBounds = renderer.bounds;
if (!isBoundsInitialized)
{
isBoundsInitialized = true;
bounds = new Bounds(rendererBounds.center, rendererBounds.size);
}
else
{
bounds.Encapsulate(rendererBounds);
}
}
}
}
float maxBounds = Mathf.Max(Mathf.Max(bounds.extents.x, bounds.extents.y), bounds.extents.z);
_camera.transform.position = bounds.center;
_camera.orthographicSize = maxBounds * 1.3f;
UnityEditorInternal.InternalEditorUtility.SetCustomLighting(lights, Color.gray);
foreach (Renderer renderer in renderers)
{
if (renderer == null)
continue;
Matrix4x4 matrix = renderer.transform.localToWorldMatrix;
Mesh mesh = null;
if (renderer.GetComponent<MeshFilter>())
{
mesh = renderer.GetComponent<MeshFilter>().sharedMesh;
}
else if (renderer is SkinnedMeshRenderer)
{
mesh = ((SkinnedMeshRenderer)renderer).sharedMesh;
}
if (mesh != null && renderer.sharedMaterials != null)
{
int submeshIndex = 0;
foreach (Material mat in renderer.sharedMaterials)
{
Graphics.DrawMesh(mesh, matrix, mat, _sampleLayer, _camera, Math.Min(submeshIndex, mesh.subMeshCount - 1),
null, ShadowCastingMode.Off, false, null, false);
submeshIndex++;
}
}
}
_camera.Render();
UnityEditorInternal.InternalEditorUtility.RemoveCustomLighting();
}
else if (runtimeData != null && runtimeData.instanceLODs != null && runtimeData.instanceLODs.Count > 0 && runtimeData.instanceLODs[0].renderers != null
#if UNITY_2022_2_OR_NEWER
&& !GPUInstancerConstants.gpuiSettings.isURP
#endif
)
{
float maxBounds = Mathf.Max(Mathf.Max(runtimeData.instanceBounds.extents.x, runtimeData.instanceBounds.extents.y), runtimeData.instanceBounds.extents.z);
_camera.transform.position = runtimeData.instanceBounds.center;
_camera.orthographicSize = maxBounds * 1.3f;
UnityEditorInternal.InternalEditorUtility.SetCustomLighting(lights, Color.gray);
for (int i = 0; i < runtimeData.instanceLODs[0].renderers.Count; i++)
{
GPUInstancerRenderer renderer = runtimeData.instanceLODs[0].renderers[i];
if (renderer.materials != null)
{
int submeshIndex = 0;
foreach (Material mat in renderer.materials)
{
Graphics.DrawMesh(renderer.mesh, renderer.transformOffset, mat, _sampleLayer, _camera, Math.Min(submeshIndex, renderer.mesh.subMeshCount - 1),
null, ShadowCastingMode.Off, false, null, false);
submeshIndex++;
}
}
}
_camera.Render();
UnityEditorInternal.InternalEditorUtility.RemoveCustomLighting();
}
#region Generate Texture
Texture2D newTx = new Texture2D(_renderTexture.width, _renderTexture.height, TextureFormat.RGBA32, true);
newTx.ReadPixels(new Rect(0, 0, _renderTexture.width, _renderTexture.height), 0, 0);
newTx.Apply();
#endregion Generate Texture
#region End Preview
RenderTexture.active = previousRenderTexture;
foreach (var light in lights)
light.enabled = false;
RenderTexture.ReleaseTemporary(_renderTexture);
#endregion End Preview
return newTx;
}
public float GetScaleFactor(float width, float height)
{
float scaleFacX = Mathf.Max(Mathf.Min(width * 2, 1024), width) / width;
float scaleFacY = Mathf.Max(Mathf.Min(height * 2, 1024), height) / height;
float result = Mathf.Min(scaleFacX, scaleFacY) * EditorGUIUtility.pixelsPerPoint;
return result;
}
protected static GameObject CreateLight()
{
GameObject lightGO = EditorUtility.CreateGameObjectWithHideFlags("PreRenderLight", HideFlags.HideAndDontSave, typeof(Light));
var light = lightGO.GetComponent<Light>();
light.type = LightType.Directional;
light.intensity = 1.0f;
light.enabled = false;
return lightGO;
}
}
}