A game about forced loneliness, made by TACStudios
1using UnityEngine;
2using UnityEditor;
3
4namespace TMPro.EditorUtilities
5{
6 /// <summary>Base class for TextMesh Pro shader GUIs.</summary>
7 public abstract class TMP_BaseShaderGUI : ShaderGUI
8 {
9 /// <summary>Representation of a #pragma shader_feature.</summary>
10 /// <description>It is assumed that the first feature option is for no keyword (underscores).</description>
11 protected class ShaderFeature
12 {
13 public string undoLabel;
14
15 public GUIContent label;
16
17 /// <summary>The keyword labels, for display. Include the no-keyword as the first option.</summary>
18 public GUIContent[] keywordLabels;
19
20 /// <summary>The shader keywords. Exclude the no-keyword option.</summary>
21 public string[] keywords;
22
23 int m_State;
24
25 public bool Active
26 {
27 get { return m_State >= 0; }
28 }
29
30 public int State
31 {
32 get { return m_State; }
33 }
34
35 public void ReadState(Material material)
36 {
37 for (int i = 0; i < keywords.Length; i++)
38 {
39 if (material.IsKeywordEnabled(keywords[i]))
40 {
41 m_State = i;
42 return;
43 }
44 }
45
46 m_State = -1;
47 }
48
49 public void SetActive(bool active, Material material)
50 {
51 m_State = active ? 0 : -1;
52 SetStateKeywords(material);
53 }
54
55 public void DoPopup(MaterialEditor editor, Material material)
56 {
57 EditorGUI.BeginChangeCheck();
58 int selection = EditorGUILayout.Popup(label, m_State + 1, keywordLabels);
59 if (EditorGUI.EndChangeCheck())
60 {
61 m_State = selection - 1;
62 editor.RegisterPropertyChangeUndo(undoLabel);
63 SetStateKeywords(material);
64 }
65 }
66
67 void SetStateKeywords(Material material)
68 {
69 for (int i = 0; i < keywords.Length; i++)
70 {
71 if (i == m_State)
72 {
73 material.EnableKeyword(keywords[i]);
74 }
75 else
76 {
77 material.DisableKeyword(keywords[i]);
78 }
79 }
80 }
81 }
82
83 static GUIContent s_TempLabel = new GUIContent();
84
85 protected static bool s_DebugExtended;
86
87 static int s_UndoRedoCount, s_LastSeenUndoRedoCount;
88
89 static float[][] s_TempFloats =
90 {
91 null, new float[1], new float[2], new float[3], new float[4]
92 };
93
94 protected static GUIContent[] s_XywhVectorLabels =
95 {
96 new GUIContent("X"),
97 new GUIContent("Y"),
98 new GUIContent("W", "Width"),
99 new GUIContent("H", "Height")
100 };
101
102 protected static GUIContent[] s_LbrtVectorLabels =
103 {
104 new GUIContent("L", "Left"),
105 new GUIContent("B", "Bottom"),
106 new GUIContent("R", "Right"),
107 new GUIContent("T", "Top")
108 };
109
110 protected static GUIContent[] s_CullingTypeLabels =
111 {
112 new GUIContent("Off"),
113 new GUIContent("Front"),
114 new GUIContent("Back")
115 };
116
117 static TMP_BaseShaderGUI()
118 {
119 // Keep track of how many undo/redo events happened.
120 Undo.undoRedoPerformed += () => s_UndoRedoCount += 1;
121 }
122
123 bool m_IsNewGUI = true;
124
125 float m_DragAndDropMinY;
126
127 protected MaterialEditor m_Editor;
128
129 protected Material m_Material;
130 private int m_ShaderID;
131
132 protected MaterialProperty[] m_Properties;
133
134 void PrepareGUI()
135 {
136 m_IsNewGUI = false;
137 ShaderUtilities.GetShaderPropertyIDs();
138
139 // New GUI just got constructed. This happens in response to a selection,
140 // but also after undo/redo events.
141 if (s_LastSeenUndoRedoCount != s_UndoRedoCount)
142 {
143 // There's been at least one undo/redo since the last time this GUI got constructed.
144 // Maybe the undo/redo was for this material? Assume that is was.
145 TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material);
146 }
147
148 s_LastSeenUndoRedoCount = s_UndoRedoCount;
149 }
150
151 public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
152 {
153 m_Editor = materialEditor;
154 m_Material = materialEditor.target as Material;
155 this.m_Properties = properties;
156
157 if (m_IsNewGUI)
158 PrepareGUI();
159
160 DoDragAndDropBegin();
161 EditorGUI.BeginChangeCheck();
162 DoGUI();
163 if (EditorGUI.EndChangeCheck())
164 {
165 TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material);
166 }
167
168 DoDragAndDropEnd();
169 }
170
171 public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
172 {
173 base.AssignNewShaderToMaterial(material, oldShader, newShader);
174
175 TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, material);
176 }
177
178 /// <summary>Override this method to create the specific shader GUI.</summary>
179 protected abstract void DoGUI();
180
181 static string[] s_PanelStateLabel = new string[] { "\t- <i>Click to collapse</i> -", "\t- <i>Click to expand</i> -" };
182
183 protected bool BeginPanel(string panel, bool expanded)
184 {
185 EditorGUI.indentLevel = 0;
186
187 EditorGUILayout.BeginVertical(EditorStyles.helpBox);
188 Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18));
189 r.x += 20;
190 r.width += 6;
191
192 bool enabled = GUI.enabled;
193 GUI.enabled = true;
194 expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle);
195 r.width -= 30;
196 EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel);
197 GUI.enabled = enabled;
198
199 EditorGUI.indentLevel += 1;
200 EditorGUI.BeginDisabledGroup(false);
201
202 return expanded;
203 }
204
205 protected bool BeginPanel(string panel, ShaderFeature feature, bool expanded, bool readState = true)
206 {
207 EditorGUI.indentLevel = 0;
208
209 if (readState)
210 {
211 feature.ReadState(m_Material);
212 }
213
214 EditorGUI.BeginChangeCheck();
215
216 EditorGUILayout.BeginVertical(EditorStyles.helpBox);
217 GUILayout.BeginHorizontal();
218
219 Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 20, GUILayout.Width(20f)));
220 bool active = EditorGUI.Toggle(r, feature.Active);
221
222 if (EditorGUI.EndChangeCheck())
223 {
224 m_Editor.RegisterPropertyChangeUndo(feature.undoLabel);
225 feature.SetActive(active, m_Material);
226 }
227
228 r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18));
229 r.width += 6;
230
231 bool enabled = GUI.enabled;
232 GUI.enabled = true;
233 expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle);
234 r.width -= 10;
235 EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel);
236 GUI.enabled = enabled;
237
238 GUILayout.EndHorizontal();
239
240 EditorGUI.indentLevel += 1;
241 EditorGUI.BeginDisabledGroup(!active);
242
243 return expanded;
244 }
245
246 public void EndPanel()
247 {
248 EditorGUI.EndDisabledGroup();
249 EditorGUI.indentLevel -= 1;
250 EditorGUILayout.EndVertical();
251 }
252
253 MaterialProperty BeginProperty(string name)
254 {
255 MaterialProperty property = FindProperty(name, m_Properties);
256 EditorGUI.BeginChangeCheck();
257 EditorGUI.showMixedValue = property.hasMixedValue;
258 m_Editor.BeginAnimatedCheck(Rect.zero, property);
259
260 return property;
261 }
262
263 bool EndProperty()
264 {
265 m_Editor.EndAnimatedCheck();
266 EditorGUI.showMixedValue = false;
267 return EditorGUI.EndChangeCheck();
268 }
269
270 protected void DoPopup(string name, string label, GUIContent[] options)
271 {
272 MaterialProperty property = BeginProperty(name);
273 s_TempLabel.text = label;
274 int index = EditorGUILayout.Popup(s_TempLabel, (int)property.floatValue, options);
275 if (EndProperty())
276 {
277 property.floatValue = index;
278 }
279 }
280
281 protected void DoCubeMap(string name, string label)
282 {
283 DoTexture(name, label, typeof(Cubemap));
284 }
285
286 protected void DoTexture2D(string name, string label, bool withTilingOffset = false, string[] speedNames = null)
287 {
288 DoTexture(name, label, typeof(Texture2D), withTilingOffset, speedNames);
289 }
290
291 void DoTexture(string name, string label, System.Type type, bool withTilingOffset = false, string[] speedNames = null)
292 {
293 float objFieldSize = 60f;
294 bool smallLayout = EditorGUIUtility.currentViewWidth <= 330f && (withTilingOffset || speedNames != null);
295 float controlHeight = smallLayout ? objFieldSize * 2 : objFieldSize;
296
297 MaterialProperty property = FindProperty(name, m_Properties);
298 m_Editor.BeginAnimatedCheck(Rect.zero, property);
299
300 Rect rect = EditorGUILayout.GetControlRect(true, controlHeight);
301 float totalWidth = rect.width;
302 rect.width = EditorGUIUtility.labelWidth + objFieldSize;
303 rect.height = objFieldSize;
304 s_TempLabel.text = label;
305
306 EditorGUI.BeginChangeCheck();
307 Object tex = EditorGUI.ObjectField(rect, s_TempLabel, property.textureValue, type, false);
308 if (EditorGUI.EndChangeCheck())
309 {
310 property.textureValue = tex as Texture;
311 }
312
313 float additionalHeight = controlHeight - objFieldSize;
314 float xOffset = smallLayout ? rect.width - objFieldSize : rect.width;
315
316 rect.y += additionalHeight;
317 rect.x += xOffset;
318 rect.width = totalWidth - xOffset;
319 rect.height = EditorGUIUtility.singleLineHeight;
320
321 if (withTilingOffset)
322 {
323 DoTilingOffset(rect, property);
324 rect.y += (rect.height + 2f) * 2f;
325 }
326
327 m_Editor.EndAnimatedCheck();
328
329 if (speedNames != null)
330 {
331 DoUVSpeed(rect, speedNames);
332 }
333 }
334
335 void DoTilingOffset(Rect rect, MaterialProperty property)
336 {
337 float labelWidth = EditorGUIUtility.labelWidth;
338 int indentLevel = EditorGUI.indentLevel;
339 EditorGUI.indentLevel = 0;
340 EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f);
341
342 Vector4 vector = property.textureScaleAndOffset;
343
344 bool changed = false;
345 float[] values = s_TempFloats[2];
346
347 s_TempLabel.text = "Tiling";
348 Rect vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel);
349 values[0] = vector.x;
350 values[1] = vector.y;
351
352 EditorGUI.BeginChangeCheck();
353 EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values);
354 if (EditorGUI.EndChangeCheck())
355 {
356 vector.x = values[0];
357 vector.y = values[1];
358 changed = true;
359 }
360
361 rect.y += rect.height + 2f;
362 s_TempLabel.text = "Offset";
363 vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel);
364 values[0] = vector.z;
365 values[1] = vector.w;
366
367 EditorGUI.BeginChangeCheck();
368 EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values);
369 if (EditorGUI.EndChangeCheck())
370 {
371 vector.z = values[0];
372 vector.w = values[1];
373 changed = true;
374 }
375
376 if (changed)
377 {
378 property.textureScaleAndOffset = vector;
379 }
380
381 EditorGUIUtility.labelWidth = labelWidth;
382 EditorGUI.indentLevel = indentLevel;
383 }
384
385 protected void DoUVSpeed(Rect rect, string[] names)
386 {
387 float labelWidth = EditorGUIUtility.labelWidth;
388 int indentLevel = EditorGUI.indentLevel;
389 EditorGUI.indentLevel = 0;
390 EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f);
391
392 s_TempLabel.text = "Speed";
393 rect = EditorGUI.PrefixLabel(rect, s_TempLabel);
394
395 EditorGUIUtility.labelWidth = 10f;
396 rect.width = rect.width * 0.5f - 2f;
397
398 if (names.Length == 1)
399 {
400 DoFloat2(rect, names[0]);
401 }
402 else
403 {
404 DoFloat(rect, names[0], "X");
405 rect.x += rect.width + 4f;
406 DoFloat(rect, names[1], "Y");
407 }
408
409 EditorGUIUtility.labelWidth = labelWidth;
410 EditorGUI.indentLevel = indentLevel;
411 }
412
413 protected void DoToggle(string name, string label)
414 {
415 MaterialProperty property = BeginProperty(name);
416 s_TempLabel.text = label;
417 bool value = EditorGUILayout.Toggle(s_TempLabel, property.floatValue == 1f);
418 if (EndProperty())
419 {
420 property.floatValue = value ? 1f : 0f;
421 }
422 }
423
424 protected void DoFloat(string name, string label)
425 {
426 MaterialProperty property = BeginProperty(name);
427 Rect rect = EditorGUILayout.GetControlRect();
428 rect.width = EditorGUIUtility.labelWidth + 55f;
429 s_TempLabel.text = label;
430 float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue);
431 if (EndProperty())
432 {
433 property.floatValue = value;
434 }
435 }
436
437 protected void DoColor(string name, string label)
438 {
439 MaterialProperty property = BeginProperty(name);
440 s_TempLabel.text = label;
441 Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, false);
442 if (EndProperty())
443 {
444 property.colorValue = value;
445 }
446 }
447
448 void DoFloat(Rect rect, string name, string label)
449 {
450 MaterialProperty property = BeginProperty(name);
451 s_TempLabel.text = label;
452 float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue);
453 if (EndProperty())
454 {
455 property.floatValue = value;
456 }
457 }
458
459 void DoFloat2(Rect rect, string name)
460 {
461 MaterialProperty property = BeginProperty(name);
462
463 float x = EditorGUI.FloatField(rect, "X", property.vectorValue.x);
464 rect.x += rect.width + 4f;
465 float y = EditorGUI.FloatField(rect, "Y", property.vectorValue.y);
466
467 if (EndProperty())
468 {
469 property.vectorValue = new Vector2(x, y);
470 }
471 }
472
473 protected void DoOffset(string name, string label)
474 {
475 MaterialProperty property = BeginProperty(name);
476 s_TempLabel.text = label;
477 Vector2 value = EditorGUI.Vector2Field(EditorGUILayout.GetControlRect(), s_TempLabel, property.vectorValue);
478 if (EndProperty())
479 {
480 property.vectorValue = value;
481 }
482 }
483
484 protected void DoSlider(string name, string label)
485 {
486 MaterialProperty property = BeginProperty(name);
487 Vector2 range = property.rangeLimits;
488 s_TempLabel.text = label;
489 float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y);
490 if (EndProperty())
491 {
492 property.floatValue = value;
493 }
494 }
495
496 protected void DoSlider(string name, Vector2 range, string label)
497 {
498 MaterialProperty property = BeginProperty(name);
499 s_TempLabel.text = label;
500 float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y);
501 if (EndProperty())
502 {
503 property.floatValue = value;
504 }
505 }
506
507 protected void DoSlider(string propertyName, string propertyField, string label)
508 {
509 MaterialProperty property = BeginProperty(propertyName);
510 Vector2 range = property.rangeLimits;
511 s_TempLabel.text = label;
512
513 Vector4 value = property.vectorValue;
514
515 switch (propertyField)
516 {
517 case "X":
518 value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y);
519 break;
520 case "Y":
521 value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y);
522 break;
523 case "Z":
524 value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y);
525 break;
526 case "W":
527 value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y);
528 break;
529 }
530
531 if (EndProperty())
532 {
533 property.vectorValue = value;
534 }
535 }
536
537 protected void DoSlider(string propertyName, string propertyField, Vector2 range, string label)
538 {
539 MaterialProperty property = BeginProperty(propertyName);
540 s_TempLabel.text = label;
541
542 Vector4 value = property.vectorValue;
543
544 switch (propertyField)
545 {
546 case "X":
547 value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y);
548 break;
549 case "Y":
550 value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y);
551 break;
552 case "Z":
553 value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y);
554 break;
555 case "W":
556 value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y);
557 break;
558 }
559
560 if (EndProperty())
561 {
562 property.vectorValue = value;
563 }
564 }
565
566 protected void DoVector2(string name, string label)
567 {
568 MaterialProperty property = BeginProperty(name);
569 s_TempLabel.text = label;
570 Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue);
571 if (EndProperty())
572 {
573 property.vectorValue = value;
574 }
575 }
576
577 protected void DoVector3(string name, string label)
578 {
579 MaterialProperty property = BeginProperty(name);
580 s_TempLabel.text = label;
581 Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue);
582 if (EndProperty())
583 {
584 property.vectorValue = value;
585 }
586 }
587
588 protected void DoVector(string name, string label, GUIContent[] subLabels)
589 {
590 MaterialProperty property = BeginProperty(name);
591 Rect rect = EditorGUILayout.GetControlRect();
592 s_TempLabel.text = label;
593 rect = EditorGUI.PrefixLabel(rect, s_TempLabel);
594 Vector4 vector = property.vectorValue;
595
596 float[] values = s_TempFloats[subLabels.Length];
597 for (int i = 0; i < subLabels.Length; i++)
598 {
599 values[i] = vector[i];
600 }
601
602 EditorGUI.MultiFloatField(rect, subLabels, values);
603 if (EndProperty())
604 {
605 for (int i = 0; i < subLabels.Length; i++)
606 {
607 vector[i] = values[i];
608 }
609
610 property.vectorValue = vector;
611 }
612 }
613
614 bool IsNewShader()
615 {
616 if (m_Material == null)
617 return false;
618
619 int currentShaderID = m_Material.shader.GetInstanceID();
620
621 if (m_ShaderID == currentShaderID)
622 return false;
623
624 m_ShaderID = currentShaderID;
625
626 return true;
627 }
628
629 void DoDragAndDropBegin()
630 {
631 m_DragAndDropMinY = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)).y;
632 }
633
634 void DoDragAndDropEnd()
635 {
636 Rect rect = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
637 Event evt = Event.current;
638
639 if (evt.type == EventType.DragUpdated)
640 {
641 DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
642 evt.Use();
643 }
644 else if (evt.type == EventType.DragPerform && Rect.MinMaxRect(rect.xMin, m_DragAndDropMinY, rect.xMax, rect.yMax).Contains(evt.mousePosition))
645 {
646 DragAndDrop.AcceptDrag();
647 evt.Use();
648 Material droppedMaterial = DragAndDrop.objectReferences[0] as Material;
649 if (droppedMaterial && droppedMaterial != m_Material)
650 {
651 PerformDrop(droppedMaterial);
652 }
653 }
654 else if (evt.type == EventType.DragExited)
655 {
656 if (IsNewShader())
657 TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material);
658 }
659 }
660
661 void PerformDrop(Material droppedMaterial)
662 {
663 Texture droppedTex = droppedMaterial.GetTexture(ShaderUtilities.ID_MainTex);
664 if (!droppedTex)
665 {
666 return;
667 }
668
669 Texture currentTex = m_Material.GetTexture(ShaderUtilities.ID_MainTex);
670 TMP_FontAsset requiredFontAsset = null;
671 if (droppedTex != currentTex)
672 {
673 requiredFontAsset = TMP_EditorUtility.FindMatchingFontAsset(droppedMaterial);
674 if (!requiredFontAsset)
675 {
676 return;
677 }
678 }
679
680 foreach (GameObject o in Selection.gameObjects)
681 {
682 if (requiredFontAsset)
683 {
684 TMP_Text textComponent = o.GetComponent<TMP_Text>();
685 if (textComponent)
686 {
687 Undo.RecordObject(textComponent, "Font Asset Change");
688 textComponent.font = requiredFontAsset;
689 }
690 }
691
692 TMPro_EventManager.ON_DRAG_AND_DROP_MATERIAL_CHANGED(o, m_Material, droppedMaterial);
693 EditorUtility.SetDirty(o);
694 }
695 }
696 }
697}