A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Reflection; 5using System.Text.RegularExpressions; 6using UnityEngine.Assertions; 7 8namespace UnityEngine.Rendering 9{ 10 public partial class DebugUI 11 { 12 /// <summary> 13 /// Generic field - will be serialized in the editor if it's not read-only 14 /// </summary> 15 /// <typeparam name="T">The type of data managed by the field.</typeparam> 16 public abstract class Field<T> : Widget, IValueField 17 { 18 /// <summary> 19 /// Getter for this field. 20 /// </summary> 21 public Func<T> getter { get; set; } 22 /// <summary> 23 /// Setter for this field. 24 /// </summary> 25 public Action<T> setter { get; set; } 26 27 // This should be an `event` but they don't play nice with object initializers in the 28 // version of C# we use. 29 /// <summary> 30 /// Callback used when the value of the field changes. 31 /// </summary> 32 public Action<Field<T>, T> onValueChanged; 33 34 /// <summary> 35 /// Function used to validate the value when updating the field. 36 /// </summary> 37 /// <param name="value">Input value.</param> 38 /// <returns>Validated value.</returns> 39 object IValueField.ValidateValue(object value) 40 { 41 return ValidateValue((T)value); 42 } 43 44 /// <summary> 45 /// Function used to validate the value when updating the field. 46 /// </summary> 47 /// <param name="value">Input value.</param> 48 /// <returns>Validated value.</returns> 49 public virtual T ValidateValue(T value) 50 { 51 return value; 52 } 53 54 /// <summary> 55 /// Get the value of the field. 56 /// </summary> 57 /// <returns>Value of the field.</returns> 58 object IValueField.GetValue() 59 { 60 return GetValue(); 61 } 62 63 /// <summary> 64 /// Get the value of the field. 65 /// </summary> 66 /// <returns>Value of the field.</returns> 67 public T GetValue() 68 { 69 Assert.IsNotNull(getter); 70 return getter(); 71 } 72 73 /// <summary> 74 /// Set the value of the field. 75 /// </summary> 76 /// <param name="value">Input value.</param> 77 public void SetValue(object value) 78 { 79 SetValue((T)value); 80 } 81 82 /// <summary> 83 /// Set the value of the field. 84 /// </summary> 85 /// <param name="value">Input value.</param> 86 public virtual void SetValue(T value) 87 { 88 Assert.IsNotNull(setter); 89 var v = ValidateValue(value); 90 91 if (v == null || !v.Equals(getter())) 92 { 93 setter(v); 94 onValueChanged?.Invoke(this, v); 95 } 96 } 97 } 98 99 /// <summary> 100 /// Boolean field. 101 /// </summary> 102 public class BoolField : Field<bool> { } 103 /// <summary> 104 /// An array of checkboxes that Unity displays in a horizontal row. 105 /// </summary> 106 public class HistoryBoolField : BoolField 107 { 108 /// <summary> 109 /// History getter for this field. 110 /// </summary> 111 public Func<bool>[] historyGetter { get; set; } 112 /// <summary> 113 /// Depth of the field's history. 114 /// </summary> 115 public int historyDepth => historyGetter?.Length ?? 0; 116 /// <summary> 117 /// Get the value of the field at a certain history index. 118 /// </summary> 119 /// <param name="historyIndex">Index of the history to query.</param> 120 /// <returns>Value of the field at the provided history index.</returns> 121 public bool GetHistoryValue(int historyIndex) 122 { 123 Assert.IsNotNull(historyGetter); 124 Assert.IsTrue(historyIndex >= 0 && historyIndex < historyGetter.Length, "out of range historyIndex"); 125 Assert.IsNotNull(historyGetter[historyIndex]); 126 return historyGetter[historyIndex](); 127 } 128 } 129 130 /// <summary> 131 /// A slider for an integer. 132 /// </summary> 133 public class IntField : Field<int> 134 { 135 /// <summary> 136 /// Minimum value function. 137 /// </summary> 138 public Func<int> min; 139 /// <summary> 140 /// Maximum value function. 141 /// </summary> 142 public Func<int> max; 143 144 // Runtime-only 145 /// <summary> 146 /// Step increment. 147 /// </summary> 148 public int incStep = 1; 149 /// <summary> 150 /// Step increment multiplier. 151 /// </summary> 152 public int intStepMult = 10; 153 154 /// <summary> 155 /// Function used to validate the value when updating the field. 156 /// </summary> 157 /// <param name="value">Input value.</param> 158 /// <returns>Validated value.</returns> 159 public override int ValidateValue(int value) 160 { 161 if (min != null) value = Mathf.Max(value, min()); 162 if (max != null) value = Mathf.Min(value, max()); 163 return value; 164 } 165 } 166 167 /// <summary> 168 /// A slider for a positive integer. 169 /// </summary> 170 public class UIntField : Field<uint> 171 { 172 /// <summary> 173 /// Minimum value function. 174 /// </summary> 175 public Func<uint> min; 176 /// <summary> 177 /// Maximum value function. 178 /// </summary> 179 public Func<uint> max; 180 181 // Runtime-only 182 /// <summary> 183 /// Step increment. 184 /// </summary> 185 public uint incStep = 1u; 186 /// <summary> 187 /// Step increment multiplier. 188 /// </summary> 189 public uint intStepMult = 10u; 190 191 /// <summary> 192 /// Function used to validate the value when updating the field. 193 /// </summary> 194 /// <param name="value">Input value.</param> 195 /// <returns>Validated value.</returns> 196 public override uint ValidateValue(uint value) 197 { 198 if (min != null) value = (uint)Mathf.Max((int)value, (int)min()); 199 if (max != null) value = (uint)Mathf.Min((int)value, (int)max()); 200 return value; 201 } 202 } 203 204 /// <summary> 205 /// A slider for a float. 206 /// </summary> 207 public class FloatField : Field<float> 208 { 209 /// <summary> 210 /// Minimum value function. 211 /// </summary> 212 public Func<float> min; 213 /// <summary> 214 /// Maximum value function. 215 /// </summary> 216 public Func<float> max; 217 218 // Runtime-only 219 /// <summary> 220 /// Step increment. 221 /// </summary> 222 public float incStep = 0.1f; 223 /// <summary> 224 /// Step increment multiplier. 225 /// </summary> 226 public float incStepMult = 10f; 227 /// <summary> 228 /// Number of decimals. 229 /// </summary> 230 public int decimals = 3; 231 232 /// <summary> 233 /// Function used to validate the value when updating the field. 234 /// </summary> 235 /// <param name="value">Input value.</param> 236 /// <returns>Validated value.</returns> 237 public override float ValidateValue(float value) 238 { 239 if (min != null) value = Mathf.Max(value, min()); 240 if (max != null) value = Mathf.Min(value, max()); 241 return value; 242 } 243 } 244 245 /// <summary> 246 /// Generic <see cref="EnumField"/> that stores enumNames and enumValues 247 /// </summary> 248 /// <typeparam name="T">The inner type of the field</typeparam> 249 public abstract class EnumField<T> : Field<T> 250 { 251 /// <summary> 252 /// List of names of the enumerator entries. 253 /// </summary> 254 public GUIContent[] enumNames; 255 256 private int[] m_EnumValues; 257 258 /// <summary> 259 /// List of values of the enumerator entries. 260 /// </summary> 261 public int[] enumValues 262 { 263 get => m_EnumValues; 264 set 265 { 266 if (value?.Distinct().Count() != value?.Count()) 267 Debug.LogWarning($"{displayName} - The values of the enum are duplicated, this might lead to a errors displaying the enum"); 268 m_EnumValues = value; 269 } 270 } 271 272 273 // Space-delimit PascalCase (https://stackoverflow.com/questions/155303/net-how-can-you-split-a-caps-delimited-string-into-an-array) 274 static Regex s_NicifyRegEx = new("([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", RegexOptions.Compiled); 275 276 /// <summary> 277 /// Automatically fills the enum names with a given <see cref="Type"/> 278 /// </summary> 279 /// <param name="enumType">The enum type</param> 280 protected void AutoFillFromType(Type enumType) 281 { 282 if (enumType == null || !enumType.IsEnum) 283 throw new ArgumentException($"{nameof(enumType)} must not be null and it must be an Enum type"); 284 285 using (ListPool<GUIContent>.Get(out var tmpNames)) 286 using (ListPool<int>.Get(out var tmpValues)) 287 { 288 var enumEntries = enumType.GetFields(BindingFlags.Public | BindingFlags.Static) 289 .Where(fieldInfo => !fieldInfo.IsDefined(typeof(ObsoleteAttribute)) && !fieldInfo.IsDefined(typeof(HideInInspector))); 290 foreach (var fieldInfo in enumEntries) 291 { 292 var description = fieldInfo.GetCustomAttribute<InspectorNameAttribute>(); 293 var displayName = new GUIContent(description == null ? s_NicifyRegEx.Replace(fieldInfo.Name, "$1 ") : description.displayName); 294 tmpNames.Add(displayName); 295 tmpValues.Add((int)Enum.Parse(enumType, fieldInfo.Name)); 296 } 297 enumNames = tmpNames.ToArray(); 298 enumValues = tmpValues.ToArray(); 299 } 300 } 301 } 302 303 /// <summary> 304 /// A dropdown that contains the values from an enum. 305 /// </summary> 306 public class EnumField : EnumField<int> 307 { 308 internal int[] quickSeparators; 309 310 private int[] m_Indexes; 311 internal int[] indexes => m_Indexes ??= Enumerable.Range(0, enumNames?.Length ?? 0).ToArray(); 312 313 /// <summary> 314 /// Get the enumeration value index. 315 /// </summary> 316 public Func<int> getIndex { get; set; } 317 /// <summary> 318 /// Set the enumeration value index. 319 /// </summary> 320 public Action<int> setIndex { get; set; } 321 322 /// <summary> 323 /// Current enumeration value index. 324 /// </summary> 325 public int currentIndex 326 { 327 get => getIndex(); 328 set => setIndex(value); 329 } 330 331 /// <summary> 332 /// Generates enumerator values and names automatically based on the provided type. 333 /// </summary> 334 public Type autoEnum 335 { 336 set 337 { 338 AutoFillFromType(value); 339 InitQuickSeparators(); 340 } 341 } 342 343 internal void InitQuickSeparators() 344 { 345 var enumNamesPrefix = enumNames.Select(x => 346 { 347 string[] splitted = x.text.Split('/'); 348 if (splitted.Length == 1) 349 return ""; 350 else 351 return splitted[0]; 352 }); 353 quickSeparators = new int[enumNamesPrefix.Distinct().Count()]; 354 string lastPrefix = null; 355 for (int i = 0, wholeNameIndex = 0; i < quickSeparators.Length; ++i) 356 { 357 var currentTestedPrefix = enumNamesPrefix.ElementAt(wholeNameIndex); 358 while (lastPrefix == currentTestedPrefix) 359 { 360 currentTestedPrefix = enumNamesPrefix.ElementAt(++wholeNameIndex); 361 } 362 lastPrefix = currentTestedPrefix; 363 quickSeparators[i] = wholeNameIndex++; 364 } 365 } 366 367 /// <summary> 368 /// Set the value of the field. 369 /// </summary> 370 /// <param name="value">Input value.</param> 371 public override void SetValue(int value) 372 { 373 Assert.IsNotNull(setter); 374 var validValue = ValidateValue(value); 375 376 // There might be cases that the value does not map the index, look for the correct index 377 var newCurrentIndex = Array.IndexOf(enumValues, validValue); 378 379 if (currentIndex != newCurrentIndex && !validValue.Equals(getter())) 380 { 381 setter(validValue); 382 onValueChanged?.Invoke(this, validValue); 383 384 if (newCurrentIndex > -1) 385 currentIndex = newCurrentIndex; 386 } 387 } 388 } 389 390 /// <summary> 391 /// A dropdown that contains a list of Unity objects. 392 /// </summary> 393 public class ObjectPopupField : Field<Object> 394 { 395 /// <summary> 396 /// Callback to obtain the elemtents of the pop up 397 /// </summary> 398 public Func<IEnumerable<Object>> getObjects { get; set; } 399 } 400 401 /// <summary> 402 /// Enumerator field with history. 403 /// </summary> 404 public class HistoryEnumField : EnumField 405 { 406 /// <summary> 407 /// History getter for this field. 408 /// </summary> 409 public Func<int>[] historyIndexGetter { get; set; } 410 /// <summary> 411 /// Depth of the field's history. 412 /// </summary> 413 public int historyDepth => historyIndexGetter?.Length ?? 0; 414 /// <summary> 415 /// Get the value of the field at a certain history index. 416 /// </summary> 417 /// <param name="historyIndex">Index of the history to query.</param> 418 /// <returns>Value of the field at the provided history index.</returns> 419 public int GetHistoryValue(int historyIndex) 420 { 421 Assert.IsNotNull(historyIndexGetter); 422 Assert.IsTrue(historyIndex >= 0 && historyIndex < historyIndexGetter.Length, "out of range historyIndex"); 423 Assert.IsNotNull(historyIndexGetter[historyIndex]); 424 return historyIndexGetter[historyIndex](); 425 } 426 } 427 428 /// <summary> 429 /// Bitfield enumeration field. 430 /// </summary> 431 public class BitField : EnumField<Enum> 432 { 433 Type m_EnumType; 434 435 /// <summary> 436 /// Generates bitfield values and names automatically based on the provided type. 437 /// </summary> 438 public Type enumType 439 { 440 get => m_EnumType; 441 set 442 { 443 m_EnumType = value; 444 AutoFillFromType(value); 445 } 446 } 447 } 448 449 /// <summary> 450 /// Maskfield enumeration field. 451 /// </summary> 452 public class MaskField : EnumField<uint> 453 { 454 /// <summary> 455 /// Fills the enum using the provided names 456 /// </summary> 457 /// <param name="names">names to fill the enum</param> 458 public void Fill(string[] names) 459 { 460 using (ListPool<GUIContent>.Get(out var tmpNames)) 461 using (ListPool<int>.Get(out var tmpValues)) 462 { 463 for (int i=0; i<(names.Length); ++i) 464 { 465 tmpNames.Add(new GUIContent(names[i])); 466 tmpValues.Add(i); 467 } 468 enumNames = tmpNames.ToArray(); 469 enumValues = tmpValues.ToArray(); 470 } 471 } 472 473 /// <summary> 474 /// Assigns a value to the maskfield. 475 /// </summary> 476 /// <param name="value">value for the maskfield</param> 477 public override void SetValue(uint value) 478 { 479 Assert.IsNotNull(setter); 480 var validValue = ValidateValue(value); 481 482 if (!validValue.Equals(getter())) 483 { 484 setter(validValue); 485 onValueChanged?.Invoke(this, validValue); 486 } 487 } 488 } 489 490 /// <summary> 491 /// Color field. 492 /// </summary> 493 public class ColorField : Field<Color> 494 { 495 /// <summary> 496 /// HDR color. 497 /// </summary> 498 public bool hdr = false; 499 /// <summary> 500 /// Show alpha of the color field. 501 /// </summary> 502 public bool showAlpha = true; 503 504 // Editor-only 505 /// <summary> 506 /// Show the color picker. 507 /// </summary> 508 public bool showPicker = true; 509 510 // Runtime-only 511 /// <summary> 512 /// Step increment. 513 /// </summary> 514 public float incStep = 0.025f; 515 /// <summary> 516 /// Step increment multiplier. 517 /// </summary> 518 public float incStepMult = 5f; 519 /// <summary> 520 /// Number of decimals. 521 /// </summary> 522 public int decimals = 3; 523 524 /// <summary> 525 /// Function used to validate the value when updating the field. 526 /// </summary> 527 /// <param name="value">Input value.</param> 528 /// <returns>Validated value.</returns> 529 public override Color ValidateValue(Color value) 530 { 531 if (!hdr) 532 { 533 value.r = Mathf.Clamp01(value.r); 534 value.g = Mathf.Clamp01(value.g); 535 value.b = Mathf.Clamp01(value.b); 536 value.a = Mathf.Clamp01(value.a); 537 } 538 539 return value; 540 } 541 } 542 543 /// <summary> 544 /// Vector2 field. 545 /// </summary> 546 public class Vector2Field : Field<Vector2> 547 { 548 // Runtime-only 549 /// <summary> 550 /// Step increment. 551 /// </summary> 552 public float incStep = 0.025f; 553 /// <summary> 554 /// Step increment multiplier. 555 /// </summary> 556 public float incStepMult = 10f; 557 /// <summary> 558 /// Number of decimals. 559 /// </summary> 560 public int decimals = 3; 561 } 562 563 /// <summary> 564 /// Vector3 field. 565 /// </summary> 566 public class Vector3Field : Field<Vector3> 567 { 568 // Runtime-only 569 /// <summary> 570 /// Step increment. 571 /// </summary> 572 public float incStep = 0.025f; 573 /// <summary> 574 /// Step increment multiplier. 575 /// </summary> 576 public float incStepMult = 10f; 577 /// <summary> 578 /// Number of decimals. 579 /// </summary> 580 public int decimals = 3; 581 } 582 583 /// <summary> 584 /// Vector4 field. 585 /// </summary> 586 public class Vector4Field : Field<Vector4> 587 { 588 // Runtime-only 589 /// <summary> 590 /// Step increment. 591 /// </summary> 592 public float incStep = 0.025f; 593 /// <summary> 594 /// Step increment multiplier. 595 /// </summary> 596 public float incStepMult = 10f; 597 /// <summary> 598 /// Number of decimals. 599 /// </summary> 600 public int decimals = 3; 601 } 602 603 /// <summary> 604 /// A field for selecting a Unity object. 605 /// </summary> 606 public class ObjectField : Field<Object> 607 { 608 /// <summary> 609 /// Object type. 610 /// </summary> 611 public Type type = typeof(Object); 612 } 613 614 /// <summary> 615 /// A list of fields for selecting Unity objects. 616 /// </summary> 617 public class ObjectListField : Field<Object[]> 618 { 619 /// <summary> 620 /// Objects type. 621 /// </summary> 622 public Type type = typeof(Object); 623 } 624 625 /// <summary> 626 /// A read-only message box with an icon. 627 /// </summary> 628 public class MessageBox : Widget 629 { 630 /// <summary> 631 /// Label style defines text color and background. 632 /// </summary> 633 public enum Style 634 { 635 /// <summary> 636 /// Info category 637 /// </summary> 638 Info, 639 /// <summary> 640 /// Warning category 641 /// </summary> 642 Warning, 643 /// <summary> 644 /// Error category 645 /// </summary> 646 Error 647 } 648 649 /// <summary> 650 /// Style used to render displayName. 651 /// </summary> 652 public Style style = Style.Info; 653 654 /// <summary> 655 /// Message Callback to feed the new message to the widget 656 /// </summary> 657 public Func<string> messageCallback = null; 658 659 /// <summary> 660 /// This obtains the message from the display name or from the message callback if it is not null 661 /// </summary> 662 public string message => messageCallback == null ? displayName : messageCallback(); 663 } 664 665 /// <summary> 666 /// Widget that will show into the Runtime UI only 667 /// Warning the user if the Runtime Debug Shaders variants are being stripped from the build. 668 /// </summary> 669 public class RuntimeDebugShadersMessageBox : MessageBox 670 { 671 /// <summary> 672 /// Constructs a <see cref="RuntimeDebugShadersMessageBox"/> 673 /// </summary> 674 public RuntimeDebugShadersMessageBox() 675 { 676 displayName = 677 "Warning: the debug shader variants are missing. Ensure that the \"Strip Runtime Debug Shaders\" option is disabled in the SRP Graphics Settings."; 678 style = DebugUI.MessageBox.Style.Warning; 679 isHiddenCallback = () => 680 { 681#if !UNITY_EDITOR 682 if (GraphicsSettings.TryGetRenderPipelineSettings<ShaderStrippingSetting>(out var shaderStrippingSetting)) 683 return !shaderStrippingSetting.stripRuntimeDebugShaders; 684#endif 685 return true; 686 }; 687 } 688 } 689 } 690}