asecli 0.6.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asecli/__init__.py +3 -0
- asecli/bridge/__init__.py +40 -0
- asecli/bridge/_editor_port_contract.py +131 -0
- asecli/bridge/_editor_primitives.py +205 -0
- asecli/bridge/_editor_spec_grammar.py +245 -0
- asecli/bridge/_editor_spec_io.py +26 -0
- asecli/bridge/_editor_spec_validation.py +218 -0
- asecli/bridge/_gui_handoff_store.py +78 -0
- asecli/bridge/_gui_project.py +35 -0
- asecli/bridge/_gui_resource_store.py +159 -0
- asecli/bridge/_gui_resource_upgrade.py +122 -0
- asecli/bridge/editor_create.py +173 -0
- asecli/bridge/editor_spec.py +250 -0
- asecli/bridge/graph_geometry.py +249 -0
- asecli/bridge/graph_geometry_parser.py +57 -0
- asecli/bridge/graph_inspect.py +136 -0
- asecli/bridge/gui_handoff.py +100 -0
- asecli/bridge/gui_presentation.py +70 -0
- asecli/bridge/gui_provider_detection.py +197 -0
- asecli/bridge/gui_runtime_probe.py +25 -0
- asecli/bridge/gui_support.py +240 -0
- asecli/bridge/gui_support_resource.py +58 -0
- asecli/bridge/mcp_client.py +231 -0
- asecli/bridge/recompile.py +107 -0
- asecli/bridge/resource_text.py +12 -0
- asecli/bridge/resources/asecli_material_gui.authoring.part00.cs.txt +238 -0
- asecli/bridge/resources/asecli_material_gui.authoring.part01.cs.txt +235 -0
- asecli/bridge/resources/asecli_material_gui.authoring.store.cs.txt +125 -0
- asecli/bridge/resources/asecli_material_gui.condition.cs.txt +99 -0
- asecli/bridge/resources/asecli_material_gui.hydration.cs.txt +105 -0
- asecli/bridge/resources/asecli_material_gui.part00.cs.txt +300 -0
- asecli/bridge/resources/asecli_material_gui.part01.cs.txt +290 -0
- asecli/bridge/resources/asecli_material_gui.reconciliation.cs.txt +174 -0
- asecli/bridge/resources/asecli_material_gui.transaction.cs.txt +108 -0
- asecli/bridge/resources/editor_create.part00.cs.txt +176 -0
- asecli/bridge/resources/editor_create.part01.cs.txt +145 -0
- asecli/bridge/resources/editor_create.part02.cs.txt +139 -0
- asecli/bridge/resources/editor_create.part03.cs.txt +161 -0
- asecli/bridge/resources/editor_create.part04.cs.txt +114 -0
- asecli/bridge/resources/wire_route.transaction.cs.txt +140 -0
- asecli/bridge/wire_route.py +127 -0
- asecli/checks/__init__.py +10 -0
- asecli/checks/checksum.py +53 -0
- asecli/checks/local_vars.py +135 -0
- asecli/checks/usage.py +141 -0
- asecli/checks/validate.py +129 -0
- asecli/cli/__init__.py +1 -0
- asecli/cli/commands.py +237 -0
- asecli/cli/commentary_command.py +158 -0
- asecli/cli/create_command.py +250 -0
- asecli/cli/custom_gui_command.py +170 -0
- asecli/cli/gui_provider.py +30 -0
- asecli/cli/gui_support_command.py +35 -0
- asecli/cli/io.py +214 -0
- asecli/cli/layout_command.py +213 -0
- asecli/cli/main.py +245 -0
- asecli/cli/recompile_metadata.py +53 -0
- asecli/cli/skill_command.py +121 -0
- asecli/cli/usage_command.py +42 -0
- asecli/core/__init__.py +84 -0
- asecli/core/comment_bounds.py +166 -0
- asecli/core/comment_layout.py +167 -0
- asecli/core/comment_metrics.py +132 -0
- asecli/core/comment_purpose.py +140 -0
- asecli/core/commentary.py +225 -0
- asecli/core/compiled_metadata.py +135 -0
- asecli/core/compiled_properties.py +190 -0
- asecli/core/custom_gui.py +250 -0
- asecli/core/custom_gui_versions.py +35 -0
- asecli/core/fishbone_placement.py +159 -0
- asecli/core/fishbone_topology.py +186 -0
- asecli/core/graph_ops.py +122 -0
- asecli/core/layout.py +174 -0
- asecli/core/layout_audit.py +237 -0
- asecli/core/layout_audit_geometry.py +247 -0
- asecli/core/layout_audit_repeated.py +44 -0
- asecli/core/layout_graph.py +113 -0
- asecli/core/local_vars.py +54 -0
- asecli/core/material_gui_condition.py +68 -0
- asecli/core/material_gui_protocol.py +35 -0
- asecli/core/material_gui_spec.py +191 -0
- asecli/core/meticulous_layout.py +114 -0
- asecli/core/model.py +237 -0
- asecli/core/property_presentation.py +233 -0
- asecli/core/wire_geometry.py +83 -0
- asecli/core/wire_router.py +248 -0
- asecli/schema/__init__.py +49 -0
- asecli/schema/data/observed.json +16 -0
- asecli/schema/data/schemas.json +14106 -0
- asecli/skills/asecli/SKILL.md +275 -0
- asecli/skills/asecli/references/layout-standard.md +99 -0
- asecli/skills/asecli/references/master-output-settings-standard.md +51 -0
- asecli/skills/asecli/references/material-property-standard.md +108 -0
- asecli-0.6.2.dist-info/METADATA +594 -0
- asecli-0.6.2.dist-info/RECORD +98 -0
- asecli-0.6.2.dist-info/WHEEL +4 -0
- asecli-0.6.2.dist-info/entry_points.txt +2 -0
- asecli-0.6.2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
|
|
2
|
+
private static Dictionary<string, PropertyMetadata> ReadMetadata(Shader shader)
|
|
3
|
+
{
|
|
4
|
+
Dictionary<string, PropertyMetadata> result =
|
|
5
|
+
new Dictionary<string, PropertyMetadata>(StringComparer.Ordinal);
|
|
6
|
+
try
|
|
7
|
+
{
|
|
8
|
+
MethodInfo countMethod = FindShaderUtilMethod("GetPropertyCount", typeof(Shader));
|
|
9
|
+
if (countMethod == null)
|
|
10
|
+
countMethod = FindShaderUtilMethod("GetShaderPropertyCount", typeof(Shader));
|
|
11
|
+
MethodInfo nameMethod = FindShaderUtilMethod("GetPropertyName", typeof(Shader), typeof(int));
|
|
12
|
+
MethodInfo attributesMethod = FindShaderUtilMethod(
|
|
13
|
+
"GetShaderPropertyAttributes", typeof(Shader), typeof(int));
|
|
14
|
+
Type handlerType = typeof(MaterialEditor).Assembly.GetType("UnityEditor.MaterialPropertyHandler");
|
|
15
|
+
MethodInfo handlerMethod = handlerType != null
|
|
16
|
+
? handlerType.GetMethod(
|
|
17
|
+
"GetHandler",
|
|
18
|
+
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static,
|
|
19
|
+
null,
|
|
20
|
+
new[] { typeof(Shader), typeof(string) },
|
|
21
|
+
null
|
|
22
|
+
)
|
|
23
|
+
: null;
|
|
24
|
+
FieldInfo decoratorsField = handlerType != null
|
|
25
|
+
? handlerType.GetField(
|
|
26
|
+
"m_DecoratorDrawers",
|
|
27
|
+
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
|
28
|
+
)
|
|
29
|
+
: null;
|
|
30
|
+
if (countMethod == null || nameMethod == null ||
|
|
31
|
+
(attributesMethod == null && (handlerMethod == null || decoratorsField == null)))
|
|
32
|
+
throw new MissingMethodException("ShaderUtil property metadata API is unavailable");
|
|
33
|
+
|
|
34
|
+
int count = Convert.ToInt32(countMethod.Invoke(null, new object[] { shader }));
|
|
35
|
+
for (int index = 0; index < count; index++)
|
|
36
|
+
{
|
|
37
|
+
string name = nameMethod.Invoke(null, new object[] { shader, index }) as string;
|
|
38
|
+
if (String.IsNullOrEmpty(name))
|
|
39
|
+
continue;
|
|
40
|
+
PropertyMetadata metadata = new PropertyMetadata();
|
|
41
|
+
if (attributesMethod != null)
|
|
42
|
+
{
|
|
43
|
+
string[] attributes = attributesMethod.Invoke(
|
|
44
|
+
null, new object[] { shader, index }) as string[];
|
|
45
|
+
if (attributes != null)
|
|
46
|
+
{
|
|
47
|
+
foreach (string attribute in attributes)
|
|
48
|
+
ApplyAttribute(metadata, attribute);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else
|
|
52
|
+
{
|
|
53
|
+
object handler = handlerMethod.Invoke(null, new object[] { shader, name });
|
|
54
|
+
IEnumerable decorators = handler != null
|
|
55
|
+
? decoratorsField.GetValue(handler) as IEnumerable
|
|
56
|
+
: null;
|
|
57
|
+
if (decorators != null)
|
|
58
|
+
{
|
|
59
|
+
foreach (object drawer in decorators)
|
|
60
|
+
{
|
|
61
|
+
IASECLIMetadataDecorator decorator = drawer as IASECLIMetadataDecorator;
|
|
62
|
+
if (decorator != null)
|
|
63
|
+
ApplyMetadata(metadata, decorator.MetadataType, decorator.MetadataValue);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Tuanjie 2022.3 has no ShaderUtil.GetShaderPropertyAttributes.
|
|
68
|
+
// Its MaterialPropertyHandler can resolve a historical marker to
|
|
69
|
+
// another package's drawer with the same unqualified type name.
|
|
70
|
+
// In that case the drawer does not implement our private interface,
|
|
71
|
+
// so recover only missing ASECLI/legacy metadata from this shader's
|
|
72
|
+
// source asset. This is input-only compatibility, never a writer.
|
|
73
|
+
PropertyMetadata sourceMetadata;
|
|
74
|
+
if (TryReadSourceMetadata(shader, name, out sourceMetadata))
|
|
75
|
+
MergeMissingMetadata(metadata, sourceMetadata);
|
|
76
|
+
}
|
|
77
|
+
result[name] = metadata;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (Exception exception)
|
|
81
|
+
{
|
|
82
|
+
if (!metadataWarningLogged)
|
|
83
|
+
{
|
|
84
|
+
metadataWarningLogged = true;
|
|
85
|
+
Debug.LogWarning("ASECLI Material GUI could not read shader attributes: " + exception.Message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private static bool TryReadSourceMetadata(
|
|
92
|
+
Shader shader,
|
|
93
|
+
string propertyName,
|
|
94
|
+
out PropertyMetadata metadata)
|
|
95
|
+
{
|
|
96
|
+
metadata = new PropertyMetadata();
|
|
97
|
+
string assetPath = AssetDatabase.GetAssetPath(shader);
|
|
98
|
+
if (String.IsNullOrEmpty(assetPath) ||
|
|
99
|
+
!assetPath.StartsWith("Assets/", StringComparison.Ordinal))
|
|
100
|
+
return false;
|
|
101
|
+
|
|
102
|
+
try
|
|
103
|
+
{
|
|
104
|
+
string projectRoot = Directory.GetParent(Application.dataPath).FullName;
|
|
105
|
+
string fullPath = Path.Combine(projectRoot, assetPath);
|
|
106
|
+
if (!File.Exists(fullPath))
|
|
107
|
+
return false;
|
|
108
|
+
|
|
109
|
+
string escapedPropertyName = Regex.Escape(propertyName);
|
|
110
|
+
Regex declarationPattern = new Regex(
|
|
111
|
+
@"(?m)^[\t ]*(?<attributes>(?:\[[^\]\r\n]*\][\t ]*)*)" +
|
|
112
|
+
escapedPropertyName +
|
|
113
|
+
@"[\t ]*\(",
|
|
114
|
+
RegexOptions.CultureInvariant
|
|
115
|
+
);
|
|
116
|
+
Match declaration = declarationPattern.Match(File.ReadAllText(fullPath));
|
|
117
|
+
if (!declaration.Success)
|
|
118
|
+
return false;
|
|
119
|
+
|
|
120
|
+
foreach (Match attribute in RawPropertyAttributePattern.Matches(
|
|
121
|
+
declaration.Groups["attributes"].Value))
|
|
122
|
+
ApplyAttribute(metadata, attribute.Value);
|
|
123
|
+
return !String.IsNullOrEmpty(metadata.Foldout) ||
|
|
124
|
+
!String.IsNullOrEmpty(metadata.Tooltip) ||
|
|
125
|
+
!String.IsNullOrEmpty(metadata.Help);
|
|
126
|
+
}
|
|
127
|
+
catch (Exception exception)
|
|
128
|
+
{
|
|
129
|
+
if (!metadataWarningLogged)
|
|
130
|
+
{
|
|
131
|
+
metadataWarningLogged = true;
|
|
132
|
+
Debug.LogWarning("ASECLI Material GUI could not read shader source metadata: " +
|
|
133
|
+
exception.Message);
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private static void MergeMissingMetadata(
|
|
140
|
+
PropertyMetadata target,
|
|
141
|
+
PropertyMetadata source)
|
|
142
|
+
{
|
|
143
|
+
if (String.IsNullOrEmpty(target.Foldout))
|
|
144
|
+
target.Foldout = source.Foldout;
|
|
145
|
+
if (String.IsNullOrEmpty(target.Tooltip))
|
|
146
|
+
target.Tooltip = source.Tooltip;
|
|
147
|
+
if (String.IsNullOrEmpty(target.Help))
|
|
148
|
+
target.Help = source.Help;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private static MethodInfo FindShaderUtilMethod(string name, params Type[] parameterTypes)
|
|
152
|
+
{
|
|
153
|
+
return typeof(ShaderUtil).GetMethod(
|
|
154
|
+
name,
|
|
155
|
+
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static,
|
|
156
|
+
null,
|
|
157
|
+
parameterTypes,
|
|
158
|
+
null
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private static void ApplyAttribute(PropertyMetadata metadata, string rawAttribute)
|
|
163
|
+
{
|
|
164
|
+
if (String.IsNullOrEmpty(rawAttribute))
|
|
165
|
+
return;
|
|
166
|
+
string value = rawAttribute.Trim();
|
|
167
|
+
if (value.StartsWith("[", StringComparison.Ordinal) &&
|
|
168
|
+
value.EndsWith("]", StringComparison.Ordinal))
|
|
169
|
+
value = value.Substring(1, value.Length - 2).Trim();
|
|
170
|
+
|
|
171
|
+
int open = value.IndexOf('(');
|
|
172
|
+
string typeName = open >= 0 ? value.Substring(0, open).Trim() : value;
|
|
173
|
+
string arguments = String.Empty;
|
|
174
|
+
if (open >= 0 && value.EndsWith(")", StringComparison.Ordinal))
|
|
175
|
+
arguments = value.Substring(open + 1, value.Length - open - 2);
|
|
176
|
+
|
|
177
|
+
ApplyMetadata(metadata, typeName, arguments);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private static void ApplyMetadata(PropertyMetadata metadata, string typeName, string arguments)
|
|
181
|
+
{
|
|
182
|
+
if (String.Equals(typeName, "ASECLIFoldout", StringComparison.OrdinalIgnoreCase) ||
|
|
183
|
+
String.Equals(typeName, "FoldoutMzgui", StringComparison.OrdinalIgnoreCase))
|
|
184
|
+
metadata.Foldout = DecodeFoldout(arguments);
|
|
185
|
+
else if (String.Equals(typeName, "ASECLITooltip", StringComparison.OrdinalIgnoreCase) ||
|
|
186
|
+
String.Equals(typeName, "TooltipMzgui", StringComparison.OrdinalIgnoreCase))
|
|
187
|
+
metadata.Tooltip = AppendLine(metadata.Tooltip, DecodeCustomUnicode(arguments));
|
|
188
|
+
else if (String.Equals(typeName, "ASECLIHelpBox", StringComparison.OrdinalIgnoreCase) ||
|
|
189
|
+
String.Equals(typeName, "HelpBoxMzgui", StringComparison.OrdinalIgnoreCase))
|
|
190
|
+
metadata.Help = AppendLine(metadata.Help, DecodeCustomUnicode(arguments));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private static string DecodeFoldout(string value)
|
|
194
|
+
{
|
|
195
|
+
return CustomUnicodePattern.Replace(
|
|
196
|
+
value,
|
|
197
|
+
match => ((char)Convert.ToInt32(match.Groups[1].Value, 16)).ToString()
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private static string DecodeCustomUnicode(string value)
|
|
202
|
+
{
|
|
203
|
+
MatchCollection matches = CustomUnicodePattern.Matches(value);
|
|
204
|
+
if (matches.Count == 0)
|
|
205
|
+
return value;
|
|
206
|
+
char[] decoded = new char[matches.Count];
|
|
207
|
+
for (int index = 0; index < matches.Count; index++)
|
|
208
|
+
decoded[index] = (char)Convert.ToInt32(matches[index].Groups[1].Value, 16);
|
|
209
|
+
return new string(decoded);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private static string AppendLine(string current, string addition)
|
|
213
|
+
{
|
|
214
|
+
if (String.IsNullOrEmpty(addition))
|
|
215
|
+
return current;
|
|
216
|
+
return String.IsNullOrEmpty(current) ? addition : current + "\n" + addition;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private static Dictionary<string, string> ReadDefaultValues(Shader shader)
|
|
220
|
+
{
|
|
221
|
+
Dictionary<string, string> result =
|
|
222
|
+
new Dictionary<string, string>(StringComparer.Ordinal);
|
|
223
|
+
Material defaultMaterial = new Material(shader);
|
|
224
|
+
defaultMaterial.hideFlags = HideFlags.HideAndDontSave;
|
|
225
|
+
try
|
|
226
|
+
{
|
|
227
|
+
MaterialProperty[] defaultProperties = MaterialEditor.GetMaterialProperties(
|
|
228
|
+
new UnityEngine.Object[] { defaultMaterial }
|
|
229
|
+
);
|
|
230
|
+
foreach (MaterialProperty property in defaultProperties)
|
|
231
|
+
result[property.name] = FormatDefaultValue(property);
|
|
232
|
+
}
|
|
233
|
+
finally
|
|
234
|
+
{
|
|
235
|
+
UnityEngine.Object.DestroyImmediate(defaultMaterial);
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private static string FormatDefaultValue(MaterialProperty property)
|
|
241
|
+
{
|
|
242
|
+
if (property.type == MaterialProperty.PropType.Color)
|
|
243
|
+
{
|
|
244
|
+
Color value = property.colorValue;
|
|
245
|
+
return Tuple(value.r, value.g, value.b, value.a);
|
|
246
|
+
}
|
|
247
|
+
if (property.type == MaterialProperty.PropType.Vector)
|
|
248
|
+
{
|
|
249
|
+
Vector4 value = property.vectorValue;
|
|
250
|
+
return Tuple(value.x, value.y, value.z, value.w);
|
|
251
|
+
}
|
|
252
|
+
if (property.type == MaterialProperty.PropType.Texture)
|
|
253
|
+
return property.textureValue != null ? property.textureValue.name : "None";
|
|
254
|
+
if (String.Equals(property.type.ToString(), "Int", StringComparison.Ordinal))
|
|
255
|
+
{
|
|
256
|
+
PropertyInfo intValueProperty = typeof(MaterialProperty).GetProperty(
|
|
257
|
+
"intValue", BindingFlags.Public | BindingFlags.Instance);
|
|
258
|
+
if (intValueProperty != null)
|
|
259
|
+
return Convert.ToInt32(intValueProperty.GetValue(property, null))
|
|
260
|
+
.ToString(CultureInfo.InvariantCulture);
|
|
261
|
+
return Math.Round(property.floatValue).ToString(CultureInfo.InvariantCulture);
|
|
262
|
+
}
|
|
263
|
+
return Number(property.floatValue);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private static string Tuple(float x, float y, float z, float w)
|
|
267
|
+
{
|
|
268
|
+
return "(" + Number(x) + ", " + Number(y) + ", " + Number(z) + ", " + Number(w) + ")";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private static string Number(float value)
|
|
272
|
+
{
|
|
273
|
+
return value.ToString("0.######", CultureInfo.InvariantCulture);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private static string BuildTooltip(string customText, string propertyName, string defaultValue)
|
|
277
|
+
{
|
|
278
|
+
string technical = "变量名: " + propertyName + "\n默认值: " + defaultValue;
|
|
279
|
+
return String.IsNullOrEmpty(customText) ? technical : customText + "\n\n" + technical;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Portable public entry point. A shader authored in a fallback project keeps
|
|
285
|
+
// working unchanged when moved to a project containing the native MZGUI package.
|
|
286
|
+
namespace MZGUI
|
|
287
|
+
{
|
|
288
|
+
public sealed class MZGUI : ASECLI.MaterialGUI.ASECLIMaterialGUI { }
|
|
289
|
+
}
|
|
290
|
+
#endif
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
|
|
2
|
+
#if UNITY_EDITOR
|
|
3
|
+
namespace ASECLI.MaterialGUI.Authoring
|
|
4
|
+
{
|
|
5
|
+
using System;
|
|
6
|
+
using System.Collections;
|
|
7
|
+
using System.Collections.Generic;
|
|
8
|
+
using System.Reflection;
|
|
9
|
+
using UnityEditor;
|
|
10
|
+
using UnityEngine;
|
|
11
|
+
|
|
12
|
+
// Moves portable Custom Attributes into the native MZGUI authoring state when
|
|
13
|
+
// and only when the complete native PropertyNode capability is available.
|
|
14
|
+
internal static class ASENativeMzguiReconciler
|
|
15
|
+
{
|
|
16
|
+
internal static bool TryMerge(
|
|
17
|
+
UnityEngine.Object node, IEnumerable<string> source, out bool handled,
|
|
18
|
+
out bool changed, out string error)
|
|
19
|
+
{
|
|
20
|
+
handled = false;
|
|
21
|
+
changed = false;
|
|
22
|
+
int nativeProviders = NativeProviderCount();
|
|
23
|
+
if (nativeProviders == 0) { error = String.Empty; return true; }
|
|
24
|
+
if (nativeProviders != 1)
|
|
25
|
+
{ handled = true; error = "检测到多个原生 MZGUI provider;已停止协调"; return false; }
|
|
26
|
+
FieldInfo attributesField = ASEReflection.Field(node.GetType(), "m_mzguiAttribs");
|
|
27
|
+
FieldInfo selectedField = ASEReflection.Field(node.GetType(), "m_selectedMzguiAttribs");
|
|
28
|
+
if (attributesField == null && selectedField == null)
|
|
29
|
+
{ error = String.Empty; return true; }
|
|
30
|
+
handled = true;
|
|
31
|
+
IList attributes = attributesField != null ? attributesField.GetValue(node) as IList : null;
|
|
32
|
+
IList selected = selectedField != null ? selectedField.GetValue(node) as IList : null;
|
|
33
|
+
if (attributes == null || selected == null)
|
|
34
|
+
{ error = "检测到不完整的原生 MZGUI 状态;已停止协调"; return false; }
|
|
35
|
+
|
|
36
|
+
Dictionary<string, string> requested = new Dictionary<string, string>(StringComparer.Ordinal);
|
|
37
|
+
foreach (string item in source)
|
|
38
|
+
{
|
|
39
|
+
string raw = (item ?? String.Empty).Trim();
|
|
40
|
+
if (raw.StartsWith("[", StringComparison.Ordinal) && raw.EndsWith("]", StringComparison.Ordinal))
|
|
41
|
+
raw = raw.Substring(1, raw.Length - 2);
|
|
42
|
+
string semantic = Semantic(raw);
|
|
43
|
+
if (semantic == null) continue;
|
|
44
|
+
raw = CanonicalRaw(semantic, raw);
|
|
45
|
+
string previous;
|
|
46
|
+
if (requested.TryGetValue(semantic, out previous) && previous != raw)
|
|
47
|
+
{ error = "同一 MZGUI 属性存在冲突值;已停止协调"; return false; }
|
|
48
|
+
requested[semantic] = raw;
|
|
49
|
+
}
|
|
50
|
+
if (requested.Count == 0) { error = String.Empty; return true; }
|
|
51
|
+
|
|
52
|
+
Dictionary<string, int> indexes = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
53
|
+
Dictionary<string, object> records = new Dictionary<string, object>(StringComparer.Ordinal);
|
|
54
|
+
for (int index = 0; index < attributes.Count; index++)
|
|
55
|
+
{
|
|
56
|
+
object record = attributes[index];
|
|
57
|
+
string name = ReadString(record, "Name");
|
|
58
|
+
string value = ReadString(record, "Attribute");
|
|
59
|
+
MethodInfo modify = record != null ? record.GetType().GetMethod(
|
|
60
|
+
"ModifyAttribute", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
|
|
61
|
+
null, new[] { typeof(string), typeof(string) }, null) : null;
|
|
62
|
+
if (String.IsNullOrEmpty(name) || value == null || modify == null) continue;
|
|
63
|
+
if (indexes.ContainsKey(name))
|
|
64
|
+
{ error = "原生 MZGUI 属性表包含重复项;已停止协调"; return false; }
|
|
65
|
+
indexes[name] = index;
|
|
66
|
+
records[name] = record;
|
|
67
|
+
}
|
|
68
|
+
foreach (string semantic in requested.Keys)
|
|
69
|
+
if (!indexes.ContainsKey(semantic))
|
|
70
|
+
{ error = "原生 MZGUI 缺少 " + semantic + " capability;已停止协调"; return false; }
|
|
71
|
+
|
|
72
|
+
Undo.RecordObject(node, "Reconcile native MZGUI attributes");
|
|
73
|
+
foreach (KeyValuePair<string, string> item in requested)
|
|
74
|
+
{
|
|
75
|
+
int index = indexes[item.Key];
|
|
76
|
+
object record = records[item.Key];
|
|
77
|
+
string bracketed = "[" + item.Value + "]";
|
|
78
|
+
if (ReadString(record, "Attribute") != bracketed)
|
|
79
|
+
{
|
|
80
|
+
record.GetType().GetMethod("ModifyAttribute",
|
|
81
|
+
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
|
|
82
|
+
null, new[] { typeof(string), typeof(string) }, null)
|
|
83
|
+
.Invoke(record, new object[] { item.Key, bracketed });
|
|
84
|
+
changed = true;
|
|
85
|
+
}
|
|
86
|
+
if (!ContainsIndex(selected, index)) { selected.Add(index); changed = true; }
|
|
87
|
+
}
|
|
88
|
+
bool removed;
|
|
89
|
+
if (!TryRemovePortable(node, out removed, out error)) return false;
|
|
90
|
+
changed |= removed;
|
|
91
|
+
FieldInfo selectedArray = ASEReflection.Field(node.GetType(), "m_selectedMzguiAttribsArr");
|
|
92
|
+
if (selectedArray != null)
|
|
93
|
+
{
|
|
94
|
+
bool[] values = new bool[attributes.Count];
|
|
95
|
+
foreach (object value in selected)
|
|
96
|
+
{ int index = Convert.ToInt32(value); if (index >= 0 && index < values.Length) values[index] = true; }
|
|
97
|
+
selectedArray.SetValue(node, values);
|
|
98
|
+
}
|
|
99
|
+
if (changed) EditorUtility.SetDirty(node);
|
|
100
|
+
error = String.Empty;
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private static string Semantic(string raw)
|
|
105
|
+
{
|
|
106
|
+
if (raw.StartsWith("FoldoutMzgui", StringComparison.Ordinal)) return "Foldout";
|
|
107
|
+
if (raw.StartsWith("TooltipMzgui", StringComparison.Ordinal)) return "Tooltip";
|
|
108
|
+
if (raw.StartsWith("HelpBoxMzgui", StringComparison.Ordinal)) return "HelpBox";
|
|
109
|
+
if (raw.StartsWith("ASECLIFoldout", StringComparison.Ordinal)) return "Foldout";
|
|
110
|
+
if (raw.StartsWith("ASECLITooltip", StringComparison.Ordinal)) return "Tooltip";
|
|
111
|
+
if (raw.StartsWith("ASECLIHelpBox", StringComparison.Ordinal)) return "HelpBox";
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private static string CanonicalRaw(string semantic, string raw)
|
|
116
|
+
{
|
|
117
|
+
string name = semantic == "Foldout" ? "FoldoutMzgui" :
|
|
118
|
+
(semantic == "Tooltip" ? "TooltipMzgui" : "HelpBoxMzgui");
|
|
119
|
+
int arguments = raw.IndexOf('(');
|
|
120
|
+
return name + (arguments >= 0 ? raw.Substring(arguments) : String.Empty);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private static string ReadString(object target, string name)
|
|
124
|
+
{
|
|
125
|
+
if (target == null) return null;
|
|
126
|
+
PropertyInfo property = target.GetType().GetProperty(name,
|
|
127
|
+
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
|
128
|
+
if (property != null) return property.GetValue(target, null) as string;
|
|
129
|
+
FieldInfo field = ASEReflection.Field(target.GetType(), name);
|
|
130
|
+
return field != null ? field.GetValue(target) as string : null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private static bool ContainsIndex(IList selected, int index)
|
|
134
|
+
{
|
|
135
|
+
foreach (object value in selected) if (Convert.ToInt32(value) == index) return true;
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private static bool TryRemovePortable(
|
|
140
|
+
UnityEngine.Object node, out bool changed, out string error)
|
|
141
|
+
{
|
|
142
|
+
changed = false;
|
|
143
|
+
IList values; FieldInfo listField; FieldInfo countField;
|
|
144
|
+
if (!ASEAttributeStore.TryStorage(
|
|
145
|
+
node, out values, out listField, out countField, out error)) return false;
|
|
146
|
+
for (int index = values.Count - 1; index >= 0; index--)
|
|
147
|
+
{
|
|
148
|
+
string raw = values[index] as string ?? String.Empty;
|
|
149
|
+
if (Semantic(raw.TrimStart('[')) == null &&
|
|
150
|
+
!raw.StartsWith("ASECLIFoldout", StringComparison.Ordinal) &&
|
|
151
|
+
!raw.StartsWith("ASECLITooltip", StringComparison.Ordinal) &&
|
|
152
|
+
!raw.StartsWith("ASECLIHelpBox", StringComparison.Ordinal)) continue;
|
|
153
|
+
values.RemoveAt(index);
|
|
154
|
+
changed = true;
|
|
155
|
+
}
|
|
156
|
+
if (changed) countField.SetValue(node, values.Count);
|
|
157
|
+
error = String.Empty;
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private static int NativeProviderCount()
|
|
162
|
+
{
|
|
163
|
+
int count = 0;
|
|
164
|
+
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
165
|
+
{
|
|
166
|
+
Type type = assembly.GetType("MZGUI.MZGUI", false);
|
|
167
|
+
if (type == null || !typeof(ShaderGUI).IsAssignableFrom(type)) continue;
|
|
168
|
+
if (type.GetInterface("ASECLI.MaterialGUI.IASECLIFallbackProvider") == null) count++;
|
|
169
|
+
}
|
|
170
|
+
return count;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
#endif
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
|
|
2
|
+
#if UNITY_EDITOR
|
|
3
|
+
namespace ASECLI.MaterialGUI.Authoring
|
|
4
|
+
{
|
|
5
|
+
using System;
|
|
6
|
+
using System.Collections;
|
|
7
|
+
using System.Collections.Generic;
|
|
8
|
+
using System.Reflection;
|
|
9
|
+
using UnityEditor;
|
|
10
|
+
using UnityEngine;
|
|
11
|
+
|
|
12
|
+
internal static class ASEApplyTransaction
|
|
13
|
+
{
|
|
14
|
+
internal static bool TryApply(
|
|
15
|
+
UnityEngine.Object node, string foldout, string tooltip, string help,
|
|
16
|
+
string enabledIf,
|
|
17
|
+
bool assignEditor, bool save, out string error)
|
|
18
|
+
{
|
|
19
|
+
List<string> attributes;
|
|
20
|
+
int attributeCount;
|
|
21
|
+
object master;
|
|
22
|
+
FieldInfo masterField;
|
|
23
|
+
string inspector;
|
|
24
|
+
string shaderPath;
|
|
25
|
+
byte[] shaderContent;
|
|
26
|
+
if (!TrySnapshot(node, out attributes, out attributeCount, out error) ||
|
|
27
|
+
!ASEReflection.TryMasterSnapshot(
|
|
28
|
+
assignEditor, out master, out masterField, out inspector, out error) ||
|
|
29
|
+
!ASEReflection.CanSave(save, out error) ||
|
|
30
|
+
!ASEReflection.TryDiskSnapshot(save, out shaderPath, out shaderContent, out error))
|
|
31
|
+
return false;
|
|
32
|
+
|
|
33
|
+
Undo.IncrementCurrentGroup();
|
|
34
|
+
int group = Undo.GetCurrentGroup();
|
|
35
|
+
Undo.SetCurrentGroupName("Apply ASECLI MZGUI attributes");
|
|
36
|
+
string stage = "write_attributes";
|
|
37
|
+
try
|
|
38
|
+
{
|
|
39
|
+
if (!ASEAttributeStore.TryWrite(
|
|
40
|
+
node, foldout, tooltip, help, enabledIf, out error))
|
|
41
|
+
throw new InvalidOperationException(error);
|
|
42
|
+
stage = "assign_editor";
|
|
43
|
+
if (assignEditor && !ASEReflection.TryAssignFallbackEditor(out error))
|
|
44
|
+
throw new InvalidOperationException(error);
|
|
45
|
+
stage = "save_shader";
|
|
46
|
+
if (save && !ASEReflection.TrySave(out error))
|
|
47
|
+
throw new InvalidOperationException(error);
|
|
48
|
+
Undo.CollapseUndoOperations(group);
|
|
49
|
+
error = String.Empty;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch (Exception exception)
|
|
53
|
+
{
|
|
54
|
+
Undo.RevertAllDownToGroup(group);
|
|
55
|
+
string attributeRollback;
|
|
56
|
+
string masterRollback;
|
|
57
|
+
string diskRollback;
|
|
58
|
+
bool restoredAttributes = TryRestore(
|
|
59
|
+
node, attributes, attributeCount, out attributeRollback);
|
|
60
|
+
bool restoredMaster = ASEReflection.TryRestoreMaster(
|
|
61
|
+
master, masterField, inspector, out masterRollback);
|
|
62
|
+
bool restoredDisk = ASEReflection.TryRestoreDisk(
|
|
63
|
+
shaderPath, shaderContent, out diskRollback);
|
|
64
|
+
string cause = exception.GetBaseException().Message;
|
|
65
|
+
error = "MZGUI 应用失败 [" + stage + "]: " + cause +
|
|
66
|
+
"; rollback=" + (restoredAttributes && restoredMaster && restoredDisk ? "ok" : "failed");
|
|
67
|
+
if (!restoredAttributes) error += "; attributes=" + attributeRollback;
|
|
68
|
+
if (!restoredMaster) error += "; editor=" + masterRollback;
|
|
69
|
+
if (!restoredDisk) error += "; disk=" + diskRollback;
|
|
70
|
+
Debug.LogError("ASECLI " + error);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private static bool TrySnapshot(
|
|
76
|
+
UnityEngine.Object node, out List<string> snapshot, out int count, out string error)
|
|
77
|
+
{
|
|
78
|
+
snapshot = new List<string>();
|
|
79
|
+
count = 0;
|
|
80
|
+
IList values; FieldInfo listField; FieldInfo countField;
|
|
81
|
+
if (!ASEAttributeStore.TryStorage(
|
|
82
|
+
node, out values, out listField, out countField, out error)) return false;
|
|
83
|
+
foreach (object item in values) snapshot.Add(item as string ?? String.Empty);
|
|
84
|
+
count = Convert.ToInt32(countField.GetValue(node));
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private static bool TryRestore(
|
|
89
|
+
UnityEngine.Object node, List<string> snapshot, int count, out string error)
|
|
90
|
+
{
|
|
91
|
+
try
|
|
92
|
+
{
|
|
93
|
+
IList values; FieldInfo listField; FieldInfo countField;
|
|
94
|
+
if (!ASEAttributeStore.TryStorage(
|
|
95
|
+
node, out values, out listField, out countField, out error)) return false;
|
|
96
|
+
values.Clear();
|
|
97
|
+
foreach (string item in snapshot) values.Add(item);
|
|
98
|
+
countField.SetValue(node, count);
|
|
99
|
+
EditorUtility.SetDirty(node);
|
|
100
|
+
error = String.Empty;
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch (Exception exception)
|
|
104
|
+
{ error = exception.GetBaseException().Message; return false; }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
#endif
|