using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; using UnityEngine.Rendering; namespace XericUI.ReflectionUIGenerator { public static class XuiavrgUtilities { /// /// 获取所有运行时成员(字段和属性) /// public static IEnumerable GetRuntimeMemberInfo(this Type type) { foreach (var field in type.GetRuntimeFields()) yield return field; foreach (var property in type.GetRuntimeProperties()) yield return property; } // ============ C# 对象: 元数据提取(纯元数据,不绑定实例)============ /// /// 从类型提取 AvrgMemberMetaData 元数据集(不含实例绑定) /// 用于构建元数据模板,可复用于不同实例 /// /// 待提取的类型 /// 纯元数据集(无实例引用,由 AccessorFactory 持有反射信息) public static AvrgMemberMetaDataSet ExtractAvrgMemberMetaData(this Type type) { if (type == null) throw new ArgumentNullException(nameof(type)); var metaDataSet = new AvrgMemberMetaDataSet(); // ★ 收集类级别 IXuiavrgAttribute — 作为所有字段的基础模板 var classAttributes = type.GetCustomAttributes() .OfType() .ToList(); foreach (var memberInfo in type.GetRuntimeMemberInfo()) { // 检查隐藏标记 var hideAttr = memberInfo.GetCustomAttribute(); if (hideAttr != null && hideAttr.Hide) continue; // 识别值类型 Type valueType = memberInfo switch { PropertyInfo pi => pi.PropertyType, FieldInfo fi => fi.FieldType, _ => typeof(object) }; // 创建元数据 — 不持有 MemberInfo 和 TargetInstance var metaData = new AvrgMemberMetaData { MemberName = memberInfo.Name, ValueType = valueType, AccessorFactory = new ReflectionAccessorFactory(memberInfo), PropertyCheckKey = valueType?.FullName, }; // ★ 第1步: 先应用类级特性(设置默认值) foreach (var classAttr in classAttributes) { classAttr.BindMetaData(metaData); } // ★ 第2步: 再应用字段级特性(可覆盖类级设置) var fieldAttributes = memberInfo.GetCustomAttributes() .OfType() .ToList(); metaData.Attributes = fieldAttributes; foreach (var fieldAttr in fieldAttributes) { fieldAttr.BindMetaData(metaData); } metaDataSet.Add(metaData); } // 处理编组合并 metaDataSet.MergeGroupEntries(); // 排序 metaDataSet.SortByFieldOrder(); return metaDataSet; } /// /// 从对象实例提取元数据并一步生成绑定(便捷方法) /// 内部调用 Type.ExtractAvrgMemberMetaData() + BuildMDVM() /// public static AvrgMvvmBindingCollection BuildBindingsFromObject( this object source, AvrgMvvmResolverConfig resolverConfig = null) { if (source == null) throw new ArgumentNullException(nameof(source)); var metaDataSet = source.GetType().ExtractAvrgMemberMetaData(); return metaDataSet.BuildMDVM(source, resolverConfig); } // ============ Material: 元数据提取 ============ /// /// 从 Material 的 Shader 属性中提取元数据集 /// /// 目标 Material /// 纯元数据集(AccessorFactory 持有 MaterialPropertyAccessorFactory) public static AvrgMemberMetaDataSet ExtractFromMaterial(this Material material) { if (material == null) throw new ArgumentNullException(nameof(material)); var shader = material.shader; if (shader == null) { Debug.LogError("[Xuiavrg] Material 没有关联的 Shader"); return null; } var metaDataSet = new AvrgMemberMetaDataSet(); int propCount = shader.GetPropertyCount(); for (int i = 0; i < propCount; i++) { var propName = shader.GetPropertyName(i); var shaderType = shader.GetPropertyType(i); var desc = shader.GetPropertyDescription(i); var flags = shader.GetPropertyFlags(i); // 跳过隐藏属性 if ((flags & UnityEngine.Rendering.ShaderPropertyFlags.HideInInspector) != 0) continue; // ShaderPropertyType → C# Type + Tag (Type valueType, XuiavrgFieldTag tag) = MapShaderPropertyType(shaderType, propName); var metaData = new AvrgMemberMetaData { MemberName = propName, ValueType = valueType, LabelName = !string.IsNullOrEmpty(desc) ? desc : FormatPropertyName(propName), Tag = tag, AccessorFactory = new MaterialPropertyAccessorFactory(propName, shaderType, valueType), }; // 读取当前值作为 DefaultValue metaData.DefaultValue = ReadMaterialProperty(material, propName, shaderType); // Range 属性:提取范围限制 if (shaderType == ShaderPropertyType.Range) { var limits = shader.GetPropertyRangeLimits(i); metaData.RangeMin = limits.x; metaData.RangeMax = limits.y; } metaDataSet.Add(metaData); } // ===== Shader Keywords 提取(只读)===== try { var keywordSpace = shader.keywordSpace; foreach (var kw in keywordSpace.keywords) { bool isEnabled = material.IsKeywordEnabled(kw.name); var kwMetaData = new AvrgMemberMetaData { MemberName = $"kwd_{kw.name}", ValueType = typeof(bool), LabelName = $"[Keyword] {kw.name}", Tag = XuiavrgFieldTag.Toggle, DefaultValue = isEnabled, AccessorFactory = new MaterialPropertyAccessorFactory( kw.name, ShaderPropertyType.Float, // Keywords 不是标准属性,用 Float 占位 typeof(bool), isReadOnly: true), }; metaDataSet.Add(kwMetaData); } } catch (Exception e) { Debug.LogWarning($"[Xuiavrg] 提取 Shader Keywords 失败: {e.Message}"); } metaDataSet.SortByFieldOrder(); return metaDataSet; } private static (Type valueType, XuiavrgFieldTag tag) MapShaderPropertyType( ShaderPropertyType shaderType, string propName) { return shaderType switch { ShaderPropertyType.Float => (typeof(float), XuiavrgFieldTag.Input), ShaderPropertyType.Range => (typeof(float), XuiavrgFieldTag.Slider), ShaderPropertyType.Int => (typeof(int), XuiavrgFieldTag.Input), ShaderPropertyType.Color => (typeof(Color), XuiavrgFieldTag.Color), ShaderPropertyType.Vector => (typeof(Vector4), XuiavrgFieldTag.Vector), ShaderPropertyType.Texture => (typeof(Texture), XuiavrgFieldTag.Texture), _ => (typeof(object), XuiavrgFieldTag.None), }; } private static object ReadMaterialProperty(Material material, string propName, ShaderPropertyType shaderType) { return shaderType switch { ShaderPropertyType.Float or ShaderPropertyType.Range => material.GetFloat(propName), ShaderPropertyType.Int => material.GetInt(propName), ShaderPropertyType.Color => material.GetColor(propName), ShaderPropertyType.Vector => material.GetVector(propName), ShaderPropertyType.Texture => material.GetTexture(propName), _ => null }; } /// /// 属性名格式化(_MainTex → MainTex, _ShadowColor → Shadow Color) /// private static string FormatPropertyName(string propName) { if (string.IsNullOrEmpty(propName)) return propName; return propName.TrimStart('_').Replace('_', ' '); } } }