using System.Collections.Generic; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UIElements; using Streamingle.Background; namespace Streamingle.Background.Editor { [CustomPropertyDrawer(typeof(SkyboxTimeController.MaterialPropertyOverride))] public class MaterialPropertyOverrideDrawer : PropertyDrawer { public override VisualElement CreatePropertyGUI(SerializedProperty property) { var root = new VisualElement(); root.style.backgroundColor = new Color(0f, 0f, 0f, 0.08f); root.style.borderBottomLeftRadius = 4; root.style.borderBottomRightRadius = 4; root.style.borderTopLeftRadius = 4; root.style.borderTopRightRadius = 4; root.style.paddingTop = 4; root.style.paddingBottom = 4; root.style.paddingLeft = 4; root.style.paddingRight = 4; root.style.marginBottom = 4; var matProp = property.FindPropertyRelative("targetMaterial"); var nameProp = property.FindPropertyRelative("propertyName"); var typeProp = property.FindPropertyRelative("propertyType"); // ── Material ── var matField = new PropertyField(matProp, "Material"); root.Add(matField); // ── Property Dropdown ── var dropdownRow = new VisualElement(); dropdownRow.style.flexDirection = FlexDirection.Row; dropdownRow.style.alignItems = Align.Center; var dropdown = new PopupField("Property", new List { "(none)" }, 0); dropdown.style.flexGrow = 1; dropdownRow.Add(dropdown); root.Add(dropdownRow); // ── Type (자동 설정되지만 표시) ── var typeField = new PropertyField(typeProp, "Type"); typeField.SetEnabled(false); root.Add(typeField); // ── Stage value containers ── var floatContainer = CreateStageRow("Float", property.FindPropertyRelative("floatStage1"), property.FindPropertyRelative("floatStage2"), property.FindPropertyRelative("floatStage3")); var colorContainer = CreateStageRow("Color", property.FindPropertyRelative("colorStage1"), property.FindPropertyRelative("colorStage2"), property.FindPropertyRelative("colorStage3")); var vectorContainer = CreateStageRow("Vector", property.FindPropertyRelative("vectorStage1"), property.FindPropertyRelative("vectorStage2"), property.FindPropertyRelative("vectorStage3")); root.Add(floatContainer); root.Add(colorContainer); root.Add(vectorContainer); // ── 셰이더 프로퍼티 목록 캐시 ── var shaderProps = new List(); // 드롭다운 rebuild void RebuildDropdown() { shaderProps.Clear(); var choices = new List { "(none)" }; var mat = matProp.objectReferenceValue as Material; if (mat != null && mat.shader != null) { var shader = mat.shader; int count = shader.GetPropertyCount(); for (int i = 0; i < count; i++) { var sType = shader.GetPropertyType(i); // Float, Range, Color, Vector만 지원 (텍스쳐 제외) if (sType != ShaderPropertyType.Float && sType != ShaderPropertyType.Range && sType != ShaderPropertyType.Color && sType != ShaderPropertyType.Vector) continue; // HideInInspector 제외 var flags = shader.GetPropertyFlags(i); if ((flags & ShaderPropertyFlags.HideInInspector) != 0) continue; var propName = shader.GetPropertyName(i); var propDesc = shader.GetPropertyDescription(i); var displayName = string.IsNullOrEmpty(propDesc) ? propName : $"{propDesc} ({propName})"; var mpType = SkyboxTimeController.MaterialPropertyType.Float; if (sType == ShaderPropertyType.Color) mpType = SkyboxTimeController.MaterialPropertyType.Color; else if (sType == ShaderPropertyType.Vector) mpType = SkyboxTimeController.MaterialPropertyType.Vector; shaderProps.Add(new ShaderPropertyInfo { name = propName, displayName = displayName, type = mpType, }); choices.Add(displayName); } } // 현재 값 찾기 int selectedIdx = 0; string currentName = nameProp.stringValue; if (!string.IsNullOrEmpty(currentName)) { for (int i = 0; i < shaderProps.Count; i++) { if (shaderProps[i].name == currentName) { selectedIdx = i + 1; // +1 for "(none)" break; } } } dropdown.choices = choices; dropdown.index = selectedIdx; } // 드롭다운 선택 시 프로퍼티 이름 + 타입 자동 설정 dropdown.RegisterValueChangedCallback(evt => { int idx = dropdown.index; if (idx <= 0 || idx > shaderProps.Count) { nameProp.stringValue = ""; nameProp.serializedObject.ApplyModifiedProperties(); } else { var info = shaderProps[idx - 1]; nameProp.stringValue = info.name; typeProp.enumValueIndex = (int)info.type; nameProp.serializedObject.ApplyModifiedProperties(); UpdateVisibility(info.type, floatContainer, colorContainer, vectorContainer); } }); // 머티리얼 변경 시 드롭다운 재구성 matField.RegisterValueChangeCallback(evt => { // schedule로 다음 프레임에 rebuild (objectReference 반영 대기) root.schedule.Execute(() => RebuildDropdown()); }); // 초기 세팅 root.schedule.Execute(() => { RebuildDropdown(); UpdateVisibility( (SkyboxTimeController.MaterialPropertyType)typeProp.enumValueIndex, floatContainer, colorContainer, vectorContainer); }); return root; } private void UpdateVisibility( SkyboxTimeController.MaterialPropertyType type, VisualElement floatContainer, VisualElement colorContainer, VisualElement vectorContainer) { floatContainer.style.display = type == SkyboxTimeController.MaterialPropertyType.Float ? DisplayStyle.Flex : DisplayStyle.None; colorContainer.style.display = type == SkyboxTimeController.MaterialPropertyType.Color ? DisplayStyle.Flex : DisplayStyle.None; vectorContainer.style.display = type == SkyboxTimeController.MaterialPropertyType.Vector ? DisplayStyle.Flex : DisplayStyle.None; } private VisualElement CreateStageRow(string label, SerializedProperty s1, SerializedProperty s2, SerializedProperty s3) { var container = new VisualElement(); container.style.marginTop = 4; var header = new Label($"── {label} Values ──"); header.style.unityFontStyleAndWeight = FontStyle.Bold; header.style.unityTextAlign = TextAnchor.MiddleCenter; header.style.marginBottom = 2; header.style.color = new Color(0.6f, 0.8f, 1f); container.Add(header); container.Add(new PropertyField(s1, "Stage 1")); container.Add(new PropertyField(s2, "Stage 2")); container.Add(new PropertyField(s3, "Stage 3")); return container; } private struct ShaderPropertyInfo { public string name; public string displayName; public SkyboxTimeController.MaterialPropertyType type; } } }