Rasagar/Library/PackageCache/com.unity.cinemachine/Editor/Windows/WaveformWindow.cs
2024-08-26 23:07:20 +03:00

236 lines
9.3 KiB
C#

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using UnityEngine.Rendering;
namespace Cinemachine.Editor
{
internal class WaveformWindow : EditorWindow
{
WaveformGenerator mWaveformGenerator;
Texture2D mScreenshot;
string mScreenshotFilename;
// Controls how frequently (in seconds) the view will update.
// Performance is really bad, so keep this as large as possible.
public static float UpdateInterval = 0.5f;
public static void SetDefaultUpdateInterval() { UpdateInterval = 0.5f; }
//[MenuItem("Window/Waveform Monitor")]
public static void OpenWindow()
{
WaveformWindow window = EditorWindow.GetWindow<WaveformWindow>(false);
window.autoRepaintOnSceneChange = true;
//window.position = new Rect(100, 100, 400, 400);
window.Show(true);
}
private void OnEnable()
{
titleContent = new GUIContent("Waveform", CinemachineSettings.CinemachineLogoTexture);
mWaveformGenerator = new WaveformGenerator();
mScreenshotFilename = Path.GetFullPath(FileUtil.GetUniqueTempPathInProject() + ".png");
ScreenCapture.CaptureScreenshot(mScreenshotFilename);
EditorApplication.update += UpdateScreenshot;
}
private void OnDisable()
{
EditorApplication.update -= UpdateScreenshot;
if (!string.IsNullOrEmpty(mScreenshotFilename) && File.Exists(mScreenshotFilename))
File.Delete(mScreenshotFilename);
mScreenshotFilename = null;
mWaveformGenerator.DestroyBuffers();
if (mScreenshot != null)
DestroyImmediate(mScreenshot);
mScreenshot = null;
}
private void OnGUI()
{
Rect rect = EditorGUILayout.GetControlRect(true);
EditorGUIUtility.labelWidth /= 2;
EditorGUI.BeginChangeCheck();
mWaveformGenerator.m_Exposure = EditorGUI.Slider(
rect, "Exposure", mWaveformGenerator.m_Exposure, 0.01f, 2);
if (EditorGUI.EndChangeCheck())
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
EditorGUIUtility.labelWidth *= 2;
rect.y += rect.height;
rect.height = position.height - rect.height;
var tex = mWaveformGenerator.Result;
if (tex != null)
GUI.DrawTexture(rect, tex);
}
float mLastUpdateTime = 0;
private void UpdateScreenshot()
{
// Don't do this too often
float now = Time.time;
if (mScreenshot != null && now - mLastUpdateTime < UpdateInterval)
return;
mLastUpdateTime = now;
if (!string.IsNullOrEmpty(mScreenshotFilename) && File.Exists(mScreenshotFilename))
{
byte[] fileData = File.ReadAllBytes(mScreenshotFilename);
if (mScreenshot == null)
mScreenshot = new Texture2D(2, 2);
mScreenshot.LoadImage(fileData); // this will auto-resize the texture dimensions.
mWaveformGenerator.RenderWaveform(mScreenshot);
// Capture the next one
ScreenCapture.CaptureScreenshot(mScreenshotFilename);
}
}
class WaveformGenerator
{
public float m_Exposure = 0.2f;
RenderTexture mOutput;
ComputeBuffer mData;
int mThreadGroupSize;
int mThreadGroupSizeX;
int mThreadGroupSizeY;
ComputeShader mWaveformCompute;
MaterialPropertyBlock mWaveformProperties;
Material mWaveformMaterial;
CommandBuffer mCmd;
static Mesh sFullscreenTriangle;
static Mesh FullscreenTriangle
{
get
{
if (sFullscreenTriangle == null)
{
sFullscreenTriangle = new Mesh { name = "Fullscreen Triangle" };
sFullscreenTriangle.SetVertices(new List<Vector3>
{
new Vector3(-1f, -1f, 0f),
new Vector3(-1f, 3f, 0f),
new Vector3( 3f, -1f, 0f)
});
sFullscreenTriangle.SetIndices(
new [] { 0, 1, 2 }, MeshTopology.Triangles, 0, false);
sFullscreenTriangle.UploadMeshData(false);
}
return sFullscreenTriangle;
}
}
public WaveformGenerator()
{
mWaveformCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>(
ScriptableObjectUtility.CinemachineRealativeInstallPath
+ "/Editor/EditorResources/CMWaveform.compute");
mWaveformProperties = new MaterialPropertyBlock();
mWaveformMaterial = new Material(AssetDatabase.LoadAssetAtPath<Shader>(
ScriptableObjectUtility.CinemachineRealativeInstallPath
+ "/Editor/EditorResources/CMWaveform.shader"))
{
name = "CMWaveformMaterial",
hideFlags = HideFlags.DontSave
};
mCmd = new CommandBuffer();
}
void CreateBuffers(int width, int height)
{
if (mOutput == null || !mOutput.IsCreated()
|| mOutput.width != width || mOutput.height != height)
{
DestroyImmediate(mOutput);
mOutput = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32)
{
anisoLevel = 0,
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp,
useMipMap = false
};
mOutput.Create();
}
int count = Mathf.CeilToInt(width / (float)mThreadGroupSizeX) * mThreadGroupSizeX * height;
if (mData == null)
mData = new ComputeBuffer(count, sizeof(uint) << 2);
else if (mData.count < count)
{
mData.Release();
mData = new ComputeBuffer(count, sizeof(uint) << 2);
}
}
public void DestroyBuffers()
{
if (mData != null)
mData.Release();
mData = null;
DestroyImmediate(mOutput);
mOutput = null;
}
public RenderTexture Result { get { return mOutput; } }
public void RenderWaveform(Texture2D source)
{
if (mWaveformMaterial == null)
return;
int width = source.width;
int height = source.height;
int histogramResolution = 256;
mThreadGroupSize = 256;
mThreadGroupSizeX = 16;
mThreadGroupSizeY = 16;
CreateBuffers(width, histogramResolution);
mCmd.Clear();
mCmd.BeginSample("CMWaveform");
var parameters = new Vector4(
width, height,
QualitySettings.activeColorSpace == ColorSpace.Linear ? 1 : 0,
histogramResolution);
// Clear the buffer on every frame
int kernel = mWaveformCompute.FindKernel("KCMWaveformClear");
mCmd.SetComputeBufferParam(mWaveformCompute, kernel, "_WaveformBuffer", mData);
mCmd.SetComputeVectorParam(mWaveformCompute, "_Params", parameters);
mCmd.DispatchCompute(mWaveformCompute, kernel,
Mathf.CeilToInt(width / (float)mThreadGroupSizeX),
Mathf.CeilToInt(histogramResolution / (float)mThreadGroupSizeY), 1);
// Gather all pixels and fill in our waveform
kernel = mWaveformCompute.FindKernel("KCMWaveformGather");
mCmd.SetComputeBufferParam(mWaveformCompute, kernel, "_WaveformBuffer", mData);
mCmd.SetComputeTextureParam(mWaveformCompute, kernel, "_Source", source);
mCmd.SetComputeVectorParam(mWaveformCompute, "_Params", parameters);
mCmd.DispatchCompute(mWaveformCompute, kernel, width,
Mathf.CeilToInt(height / (float)mThreadGroupSize), 1);
// Generate the waveform texture
float exposure = Mathf.Max(0f, m_Exposure);
exposure *= (float)histogramResolution / height;
mWaveformProperties.SetVector(Shader.PropertyToID("_Params"),
new Vector4(width, histogramResolution, exposure, 0f));
mWaveformProperties.SetBuffer(Shader.PropertyToID("_WaveformBuffer"), mData);
mCmd.SetRenderTarget(mOutput);
mCmd.DrawMesh(
FullscreenTriangle, Matrix4x4.identity,
mWaveformMaterial, 0, 0, mWaveformProperties);
mCmd.EndSample("CMWaveform");
Graphics.ExecuteCommandBuffer(mCmd);
}
}
}
}