diff options
| author | yum <yum.food.vr@gmail.com> | 2025-07-30 13:09:19 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2025-07-30 13:16:57 -0700 |
| commit | 65797304fb21181e1fcbd45dfebd0d2cf159ea6b (patch) | |
| tree | e5580c68d1f1f791d5a801ede40b83dce92cc745 /Scripts/Editor/LinearPipelineEditor.cs | |
| parent | 341ea861c8189cefe8689bd41d8adbe2cd2d87b2 (diff) | |
- Add a script to create a pipeline & assets in a single click
- Add pipeline executor concept. This exists to (theoretically) let
users merge the results of multiple pipelines.
Diffstat (limited to 'Scripts/Editor/LinearPipelineEditor.cs')
| -rw-r--r-- | Scripts/Editor/LinearPipelineEditor.cs | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/Scripts/Editor/LinearPipelineEditor.cs b/Scripts/Editor/LinearPipelineEditor.cs new file mode 100644 index 0000000..f14eb8e --- /dev/null +++ b/Scripts/Editor/LinearPipelineEditor.cs @@ -0,0 +1,339 @@ +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; +using System.IO; +using System.Collections.Generic; + +[CustomEditor(typeof(LinearPipeline))] +public class LinearPipelineEditor : Editor +{ + private const string MATERIAL_SUFFIX = "_Mat"; + private const string TEXTURE_SUFFIX = "_Tex"; + + private SerializedProperty pipelineGeneratedPathProperty; + private SerializedProperty initialStateProperty; + + private static Shader shader; + private static int radix = 16; + private static int fftResolution = 256; + private static bool isInverse = false; + private static bool doBothFFTAndInverse = false; + private static int preFFTStages = 0; + private static int postFFTStages = 0; + + private void OnEnable() + { + pipelineGeneratedPathProperty = serializedObject.FindProperty("pipelineGeneratedPath"); + + initialStateProperty = serializedObject.FindProperty("initialState"); + + // Set default shader if not already set + if (shader == null) + { + shader = Shader.Find("yum_food/fft"); + } + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(serializedObject.FindProperty("materials"), true); + EditorGUILayout.PropertyField(serializedObject.FindProperty("renderTextures"), true); + + LinearPipeline pipeline = (LinearPipeline)target; + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Pipeline Generation", EditorStyles.boldLabel); + + EditorGUILayout.PropertyField(pipelineGeneratedPathProperty); + EditorGUILayout.PropertyField(initialStateProperty); + + serializedObject.ApplyModifiedProperties(); + + shader = EditorGUILayout.ObjectField("Shader", shader, typeof(Shader), false) as Shader; + radix = Mathf.Max(2, EditorGUILayout.IntField("Radix", radix)); + fftResolution = Mathf.Max(2, EditorGUILayout.IntField("FFT Resolution (N)", fftResolution)); + + EditorGUI.BeginDisabledGroup(doBothFFTAndInverse); + isInverse = EditorGUILayout.Toggle("Inverse FFT", isInverse); + EditorGUI.EndDisabledGroup(); + + doBothFFTAndInverse = EditorGUILayout.Toggle("Both FFT and Inverse FFT", doBothFFTAndInverse); + preFFTStages = Mathf.Max(0, EditorGUILayout.IntField("Pre-FFT Stages", preFFTStages)); + postFFTStages = Mathf.Max(0, EditorGUILayout.IntField("Post-FFT Stages", postFFTStages)); + + int fftStages = CalculateFFTStages(fftResolution, radix); + EditorGUILayout.HelpBox($"FFT stages: {fftStages} (including bit reverse)", MessageType.Info); + + EditorGUILayout.Space(); + + GUI.enabled = shader != null; + if (GUILayout.Button("Create Pipeline", GUILayout.Height(30))) + { + CreatePipeline(pipeline); + } + GUI.enabled = true; + + if (shader == null) + { + EditorGUILayout.HelpBox("Please specify a shader to create the pipeline.", MessageType.Warning); + } + } + + private int CalculateFFTStages(int n, int radix) + { + return Mathf.CeilToInt(Mathf.Log(n) / Mathf.Log(radix)) * 2 + 1; + } + + private void CreatePipeline(LinearPipeline pipeline) + { + string pipelineName = pipeline.gameObject.name; + string pipelinePath = Path.Combine(pipeline.pipelineGeneratedPath, pipelineName); + + // Ensure directories exist + if (!AssetDatabase.IsValidFolder(pipeline.pipelineGeneratedPath)) + { + CreateFolderRecursive(pipeline.pipelineGeneratedPath); + } + + if (!AssetDatabase.IsValidFolder(pipelinePath)) + { + AssetDatabase.CreateFolder(pipeline.pipelineGeneratedPath, pipelineName); + } + + Undo.RegisterFullObjectHierarchyUndo(pipeline.gameObject, "Create Pipeline"); + + // Clear existing children + ClearChildren(pipeline.transform); + + // Generate stage names + List<string> stageNames = GenerateStageNames(); + + // Create arrays for materials and textures + Material[] materials = new Material[stageNames.Count]; + RenderTexture[] textures = new RenderTexture[stageNames.Count]; + + // Create each stage + for (int i = 0; i < stageNames.Count; i++) + { + CreateStage(pipeline.transform, stageNames[i], pipelinePath, i, ref materials, ref textures); + } + + // Assign textures to materials + AssignTexturesToMaterials(materials, textures, pipeline.initialState); + + // Update pipeline component + pipeline.materials = materials; + pipeline.renderTextures = textures; + + EditorUtility.SetDirty(pipeline); + AssetDatabase.SaveAssets(); + + Debug.Log($"[LinearPipeline] Created pipeline '{pipelineName}' with {materials.Length} stages"); + } + + private List<string> GenerateStageNames() + { + List<string> names = new List<string>(); + int totalFFTStages = CalculateFFTStages(fftResolution, radix); + + // Pre-FFT stages + for (int i = 0; i < preFFTStages; i++) + { + names.Add($"Pre_Stage_{i:D2}"); + } + + // FFT stages + if (doBothFFTAndInverse || !isInverse) + { + for (int i = 0; i < totalFFTStages; i++) + { + names.Add($"FFT_Stage_{i:D2}"); + } + } + + // Inverse FFT stages + if (doBothFFTAndInverse || isInverse) + { + for (int i = 0; i < totalFFTStages; i++) + { + names.Add($"IFFT_Stage_{i:D2}"); + } + } + + // Post-FFT stages + for (int i = 0; i < postFFTStages; i++) + { + names.Add($"Post_Stage_{i:D2}"); + } + + return names; + } + + private void CreateStage(Transform parent, string stageName, string basePath, int index, + ref Material[] materials, ref RenderTexture[] textures) + { + // Create stage GameObject as a quad + GameObject stageGO = GameObject.CreatePrimitive(PrimitiveType.Quad); + stageGO.name = stageName; + stageGO.transform.SetParent(parent); + stageGO.transform.localPosition = new Vector3(index * 1.0f, 0, 0); // Space 1 meter apart on X axis + stageGO.transform.localRotation = Quaternion.identity; + stageGO.transform.localScale = Vector3.one; + + DestroyImmediate(stageGO.GetComponent<MeshCollider>()); + + // Create or update material + string materialPath = $"{basePath}/{stageName}{MATERIAL_SUFFIX}.mat"; + Material material = AssetDatabase.LoadAssetAtPath<Material>(materialPath); + + if (material == null) + { + material = new Material(shader); + AssetDatabase.CreateAsset(material, materialPath); + } + else + { + material.shader = shader; + EditorUtility.SetDirty(material); + } + + // Set shader properties based on stage type + ConfigureMaterialProperties(material, stageName); + + // Create or update render texture + string texturePath = $"{basePath}/{stageName}{TEXTURE_SUFFIX}.renderTexture"; + RenderTexture texture = AssetDatabase.LoadAssetAtPath<RenderTexture>(texturePath); + + if (texture == null) + { + texture = new RenderTexture(fftResolution, fftResolution, 0, RenderTextureFormat.ARGBFloat) + { + filterMode = FilterMode.Point, + wrapMode = TextureWrapMode.Clamp + }; + AssetDatabase.CreateAsset(texture, texturePath); + } + else + { + texture.width = fftResolution; + texture.height = fftResolution; + EditorUtility.SetDirty(texture); + } + + // Assign material to renderer + MeshRenderer renderer = stageGO.GetComponent<MeshRenderer>(); + renderer.sharedMaterial = material; + + // Store in arrays + materials[index] = material; + textures[index] = texture; + } + + private void ConfigureMaterialProperties(Material material, string stageName) + { + // Set common properties + material.SetInt("_N", fftResolution); + material.SetInt("_Radix", radix); + + // Reset all flags + material.SetFloat("_Passthrough", 0f); + material.SetFloat("_LDS", 0f); + material.SetFloat("_Luminance", 0f); + material.SetFloat("_Inverse", 0f); + material.SetFloat("_BitReversal", 0f); + + // Configure based on stage type + if (stageName.StartsWith("Pre_Stage_")) + { + // Pre-processing stages - set as passthrough + material.SetFloat("_Passthrough", 1f); + material.SetInt("_Stage", 0); + } + else if (stageName.StartsWith("FFT_Stage_")) + { + // Extract stage number + string stageNumStr = stageName.Replace("FFT_Stage_", ""); + if (int.TryParse(stageNumStr, out int stageNum)) + { + material.SetInt("_Stage", stageNum); + + // Last stage is bit reversal + int totalStages = CalculateFFTStages(fftResolution, radix); + if (stageNum == totalStages - 1) + { + material.SetFloat("_BitReversal", 1f); + } + } + material.SetFloat("_Inverse", 0f); + } + else if (stageName.StartsWith("IFFT_Stage_")) + { + // Extract stage number + string stageNumStr = stageName.Replace("IFFT_Stage_", ""); + if (int.TryParse(stageNumStr, out int stageNum)) + { + material.SetInt("_Stage", stageNum); + + // Last stage is bit reversal + int totalStages = CalculateFFTStages(fftResolution, radix); + if (stageNum == totalStages - 1) + { + material.SetFloat("_BitReversal", 1f); + } + } + material.SetFloat("_Inverse", 1f); + } + else if (stageName.StartsWith("Post_Stage_")) + { + // Post-processing stages - set as passthrough + material.SetFloat("_Passthrough", 1f); + material.SetInt("_Stage", 0); + } + + EditorUtility.SetDirty(material); + } + + private void AssignTexturesToMaterials(Material[] materials, RenderTexture[] textures, Texture initialState) + { + for (int i = 0; i < materials.Length; i++) + { + Texture inputTexture = (i == 0) ? initialState : textures[i - 1]; + + if (inputTexture != null) + { + materials[i].SetTexture("_MainTex", inputTexture); + } + + EditorUtility.SetDirty(materials[i]); + } + } + + private void ClearChildren(Transform parent) + { + while (parent.childCount > 0) + { + DestroyImmediate(parent.GetChild(0).gameObject); + } + } + + private void CreateFolderRecursive(string path) + { + string[] folders = path.Split('/'); + string currentPath = folders[0]; + + for (int i = 1; i < folders.Length; i++) + { + string nextPath = Path.Combine(currentPath, folders[i]); + if (!AssetDatabase.IsValidFolder(nextPath)) + { + AssetDatabase.CreateFolder(currentPath, folders[i]); + } + currentPath = nextPath; + } + } +} + +#endif |
