initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
|
@ -0,0 +1,7 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if ENABLE_HYBRID_RENDERER_V2 && !HYBRID_0_6_0_OR_NEWER
|
||||
#error Core SRP 10.0.0 or newer with Hybrid Renderer V2 requires at least version 0.6.0 of com.unity.rendering.hybrid
|
||||
#endif
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Editor")]
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: af2a485654b7642d987c8758da936d55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e99921594db38ce4cbb616f0ca87148d
|
||||
folderAsset: yes
|
||||
timeCreated: 1488370960
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility component allowing users to setup many different static camera and cycle through their positions using the Debug Window.
|
||||
/// </summary>
|
||||
[CoreRPHelpURLAttribute("Camera-Switcher")]
|
||||
public class CameraSwitcher : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// List of target cameras.
|
||||
/// </summary>
|
||||
public Camera[] m_Cameras;
|
||||
|
||||
private int m_CurrentCameraIndex = -1;
|
||||
private Camera m_OriginalCamera = null;
|
||||
private Vector3 m_OriginalCameraPosition;
|
||||
private Quaternion m_OriginalCameraRotation;
|
||||
private Camera m_CurrentCamera = null;
|
||||
|
||||
GUIContent[] m_CameraNames = null;
|
||||
int[] m_CameraIndices = null;
|
||||
|
||||
DebugUI.EnumField m_DebugEntry;
|
||||
|
||||
//saved enum fields for when repainting
|
||||
int m_DebugEntryEnumIndex;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_OriginalCamera = GetComponent<Camera>();
|
||||
m_CurrentCamera = m_OriginalCamera;
|
||||
|
||||
if (m_OriginalCamera == null)
|
||||
{
|
||||
Debug.LogError("Camera Switcher needs a Camera component attached");
|
||||
return;
|
||||
}
|
||||
|
||||
m_CurrentCameraIndex = GetCameraCount() - 1;
|
||||
|
||||
m_CameraNames = new GUIContent[GetCameraCount()];
|
||||
m_CameraIndices = new int[GetCameraCount()];
|
||||
|
||||
for (int i = 0; i < m_Cameras.Length; ++i)
|
||||
{
|
||||
Camera cam = m_Cameras[i];
|
||||
if (cam != null)
|
||||
{
|
||||
m_CameraNames[i] = new GUIContent(cam.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CameraNames[i] = new GUIContent("null");
|
||||
}
|
||||
m_CameraIndices[i] = i;
|
||||
}
|
||||
|
||||
m_CameraNames[GetCameraCount() - 1] = new GUIContent("Original Camera");
|
||||
m_CameraIndices[GetCameraCount() - 1] = GetCameraCount() - 1;
|
||||
|
||||
m_DebugEntry = new DebugUI.EnumField { displayName = "Camera Switcher", getter = () => m_CurrentCameraIndex, setter = value => SetCameraIndex(value), enumNames = m_CameraNames, enumValues = m_CameraIndices, getIndex = () => m_DebugEntryEnumIndex, setIndex = value => m_DebugEntryEnumIndex = value };
|
||||
var panel = DebugManager.instance.GetPanel("Camera", true);
|
||||
panel.children.Add(m_DebugEntry);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (m_DebugEntry != null && m_DebugEntry.panel != null)
|
||||
{
|
||||
var panel = m_DebugEntry.panel;
|
||||
panel.children.Remove(m_DebugEntry);
|
||||
}
|
||||
}
|
||||
|
||||
int GetCameraCount()
|
||||
{
|
||||
return m_Cameras.Length + 1; // We need +1 for handling the original camera.
|
||||
}
|
||||
|
||||
Camera GetNextCamera()
|
||||
{
|
||||
if (m_CurrentCameraIndex == m_Cameras.Length)
|
||||
return m_OriginalCamera;
|
||||
else
|
||||
return m_Cameras[m_CurrentCameraIndex];
|
||||
}
|
||||
|
||||
void SetCameraIndex(int index)
|
||||
{
|
||||
if (index > 0 && index < GetCameraCount())
|
||||
{
|
||||
m_CurrentCameraIndex = index;
|
||||
|
||||
if (m_CurrentCamera == m_OriginalCamera)
|
||||
{
|
||||
m_OriginalCameraPosition = m_OriginalCamera.transform.position;
|
||||
m_OriginalCameraRotation = m_OriginalCamera.transform.rotation;
|
||||
}
|
||||
|
||||
m_CurrentCamera = GetNextCamera();
|
||||
|
||||
if (m_CurrentCamera != null)
|
||||
{
|
||||
// If we witch back to the original camera, put back the transform in it.
|
||||
if (m_CurrentCamera == m_OriginalCamera)
|
||||
{
|
||||
m_OriginalCamera.transform.position = m_OriginalCameraPosition;
|
||||
m_OriginalCamera.transform.rotation = m_OriginalCameraRotation;
|
||||
}
|
||||
|
||||
transform.position = m_CurrentCamera.transform.position;
|
||||
transform.rotation = m_CurrentCamera.transform.rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f52d1e5345c395641b482217c8804ff9
|
||||
timeCreated: 1488380874
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
|
||||
#define USE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility Free Camera component.
|
||||
/// </summary>
|
||||
[CoreRPHelpURLAttribute("Free-Camera")]
|
||||
public class FreeCamera : MonoBehaviour
|
||||
{
|
||||
const float k_MouseSensitivityMultiplier = 0.01f;
|
||||
|
||||
/// <summary>
|
||||
/// Rotation speed when using a controller.
|
||||
/// </summary>
|
||||
public float m_LookSpeedController = 120f;
|
||||
/// <summary>
|
||||
/// Rotation speed when using the mouse.
|
||||
/// </summary>
|
||||
public float m_LookSpeedMouse = 4.0f;
|
||||
/// <summary>
|
||||
/// Movement speed.
|
||||
/// </summary>
|
||||
public float m_MoveSpeed = 10.0f;
|
||||
/// <summary>
|
||||
/// Value added to the speed when incrementing.
|
||||
/// </summary>
|
||||
public float m_MoveSpeedIncrement = 2.5f;
|
||||
/// <summary>
|
||||
/// Scale factor of the turbo mode.
|
||||
/// </summary>
|
||||
public float m_Turbo = 10.0f;
|
||||
|
||||
#if !USE_INPUT_SYSTEM
|
||||
private static string kMouseX = "Mouse X";
|
||||
private static string kMouseY = "Mouse Y";
|
||||
private static string kRightStickX = "Controller Right Stick X";
|
||||
private static string kRightStickY = "Controller Right Stick Y";
|
||||
private static string kVertical = "Vertical";
|
||||
private static string kHorizontal = "Horizontal";
|
||||
|
||||
private static string kYAxis = "YAxis";
|
||||
private static string kSpeedAxis = "Speed Axis";
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
InputAction lookAction;
|
||||
InputAction moveAction;
|
||||
InputAction speedAction;
|
||||
InputAction yMoveAction;
|
||||
#endif
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
RegisterInputs();
|
||||
}
|
||||
|
||||
void RegisterInputs()
|
||||
{
|
||||
#if USE_INPUT_SYSTEM
|
||||
var map = new InputActionMap("Free Camera");
|
||||
|
||||
lookAction = map.AddAction("look", binding: "<Mouse>/delta");
|
||||
moveAction = map.AddAction("move", binding: "<Gamepad>/leftStick");
|
||||
speedAction = map.AddAction("speed", binding: "<Gamepad>/dpad");
|
||||
yMoveAction = map.AddAction("yMove");
|
||||
|
||||
lookAction.AddBinding("<Gamepad>/rightStick").WithProcessor("scaleVector2(x=15, y=15)");
|
||||
moveAction.AddCompositeBinding("Dpad")
|
||||
.With("Up", "<Keyboard>/w")
|
||||
.With("Up", "<Keyboard>/upArrow")
|
||||
.With("Down", "<Keyboard>/s")
|
||||
.With("Down", "<Keyboard>/downArrow")
|
||||
.With("Left", "<Keyboard>/a")
|
||||
.With("Left", "<Keyboard>/leftArrow")
|
||||
.With("Right", "<Keyboard>/d")
|
||||
.With("Right", "<Keyboard>/rightArrow");
|
||||
speedAction.AddCompositeBinding("Dpad")
|
||||
.With("Up", "<Keyboard>/home")
|
||||
.With("Down", "<Keyboard>/end");
|
||||
yMoveAction.AddCompositeBinding("Dpad")
|
||||
.With("Up", "<Keyboard>/pageUp")
|
||||
.With("Down", "<Keyboard>/pageDown")
|
||||
.With("Up", "<Keyboard>/e")
|
||||
.With("Down", "<Keyboard>/q")
|
||||
.With("Up", "<Gamepad>/rightshoulder")
|
||||
.With("Down", "<Gamepad>/leftshoulder");
|
||||
|
||||
moveAction.Enable();
|
||||
lookAction.Enable();
|
||||
speedAction.Enable();
|
||||
yMoveAction.Enable();
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR && !USE_INPUT_SYSTEM
|
||||
List<InputManagerEntry> inputEntries = new List<InputManagerEntry>();
|
||||
|
||||
// Add new bindings
|
||||
inputEntries.Add(new InputManagerEntry { name = kRightStickX, kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Fourth, sensitivity = 1.0f, gravity = 1.0f, deadZone = 0.2f });
|
||||
inputEntries.Add(new InputManagerEntry { name = kRightStickY, kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Fifth, sensitivity = 1.0f, gravity = 1.0f, deadZone = 0.2f, invert = true });
|
||||
|
||||
inputEntries.Add(new InputManagerEntry { name = kYAxis, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "page up", altBtnPositive = "joystick button 5", btnNegative = "page down", altBtnNegative = "joystick button 4", gravity = 1000.0f, deadZone = 0.001f, sensitivity = 1000.0f });
|
||||
inputEntries.Add(new InputManagerEntry { name = kYAxis, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "q", btnNegative = "e", gravity = 1000.0f, deadZone = 0.001f, sensitivity = 1000.0f });
|
||||
|
||||
inputEntries.Add(new InputManagerEntry { name = kSpeedAxis, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "home", btnNegative = "end", gravity = 1000.0f, deadZone = 0.001f, sensitivity = 1000.0f });
|
||||
inputEntries.Add(new InputManagerEntry { name = kSpeedAxis, kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Seventh, gravity = 1000.0f, deadZone = 0.001f, sensitivity = 1000.0f });
|
||||
|
||||
InputRegistering.RegisterInputs(inputEntries);
|
||||
#endif
|
||||
}
|
||||
|
||||
float inputRotateAxisX, inputRotateAxisY;
|
||||
float inputChangeSpeed;
|
||||
float inputVertical, inputHorizontal, inputYAxis;
|
||||
bool leftShiftBoost, leftShift, fire1;
|
||||
|
||||
void UpdateInputs()
|
||||
{
|
||||
inputRotateAxisX = 0.0f;
|
||||
inputRotateAxisY = 0.0f;
|
||||
leftShiftBoost = false;
|
||||
fire1 = false;
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
var lookDelta = lookAction.ReadValue<Vector2>();
|
||||
inputRotateAxisX = lookDelta.x * m_LookSpeedMouse * k_MouseSensitivityMultiplier;
|
||||
inputRotateAxisY = lookDelta.y * m_LookSpeedMouse * k_MouseSensitivityMultiplier;
|
||||
|
||||
leftShift = Keyboard.current.leftShiftKey.isPressed;
|
||||
fire1 = Mouse.current?.leftButton?.isPressed == true || Gamepad.current?.xButton?.isPressed == true;
|
||||
|
||||
inputChangeSpeed = speedAction.ReadValue<Vector2>().y;
|
||||
|
||||
var moveDelta = moveAction.ReadValue<Vector2>();
|
||||
inputVertical = moveDelta.y;
|
||||
inputHorizontal = moveDelta.x;
|
||||
inputYAxis = yMoveAction.ReadValue<Vector2>().y;
|
||||
#else
|
||||
if (Input.GetMouseButton(1))
|
||||
{
|
||||
leftShiftBoost = true;
|
||||
inputRotateAxisX = Input.GetAxis(kMouseX) * m_LookSpeedMouse;
|
||||
inputRotateAxisY = Input.GetAxis(kMouseY) * m_LookSpeedMouse;
|
||||
}
|
||||
inputRotateAxisX += (Input.GetAxis(kRightStickX) * m_LookSpeedController * k_MouseSensitivityMultiplier);
|
||||
inputRotateAxisY += (Input.GetAxis(kRightStickY) * m_LookSpeedController * k_MouseSensitivityMultiplier);
|
||||
|
||||
leftShift = Input.GetKey(KeyCode.LeftShift);
|
||||
fire1 = Input.GetAxis("Fire1") > 0.0f;
|
||||
|
||||
inputChangeSpeed = Input.GetAxis(kSpeedAxis);
|
||||
|
||||
inputVertical = Input.GetAxis(kVertical);
|
||||
inputHorizontal = Input.GetAxis(kHorizontal);
|
||||
inputYAxis = Input.GetAxis(kYAxis);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// If the debug menu is running, we don't want to conflict with its inputs.
|
||||
if (DebugManager.instance.displayRuntimeUI)
|
||||
return;
|
||||
|
||||
UpdateInputs();
|
||||
|
||||
if (inputChangeSpeed != 0.0f)
|
||||
{
|
||||
m_MoveSpeed += inputChangeSpeed * m_MoveSpeedIncrement;
|
||||
if (m_MoveSpeed < m_MoveSpeedIncrement) m_MoveSpeed = m_MoveSpeedIncrement;
|
||||
}
|
||||
|
||||
bool moved = inputRotateAxisX != 0.0f || inputRotateAxisY != 0.0f || inputVertical != 0.0f || inputHorizontal != 0.0f || inputYAxis != 0.0f;
|
||||
if (moved)
|
||||
{
|
||||
float rotationX = transform.localEulerAngles.x;
|
||||
float newRotationY = transform.localEulerAngles.y + inputRotateAxisX;
|
||||
|
||||
// Weird clamping code due to weird Euler angle mapping...
|
||||
float newRotationX = (rotationX - inputRotateAxisY);
|
||||
if (rotationX <= 90.0f && newRotationX >= 0.0f)
|
||||
newRotationX = Mathf.Clamp(newRotationX, 0.0f, 90.0f);
|
||||
if (rotationX >= 270.0f)
|
||||
newRotationX = Mathf.Clamp(newRotationX, 270.0f, 360.0f);
|
||||
|
||||
transform.localRotation = Quaternion.Euler(newRotationX, newRotationY, transform.localEulerAngles.z);
|
||||
|
||||
float moveSpeed = Time.deltaTime * m_MoveSpeed;
|
||||
if (fire1 || leftShiftBoost && leftShift)
|
||||
moveSpeed *= m_Turbo;
|
||||
transform.position += transform.forward * moveSpeed * inputVertical;
|
||||
transform.position += transform.right * moveSpeed * inputHorizontal;
|
||||
transform.position += Vector3.up * moveSpeed * inputYAxis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 618b0e3f6c65dd247a4a016150006c57
|
||||
timeCreated: 1488370968
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e58bd4f13d26cde488393d3a2e9bf9cd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Command Buffer Pool
|
||||
/// </summary>
|
||||
public static class CommandBufferPool
|
||||
{
|
||||
static ObjectPool<CommandBuffer> s_BufferPool = new ObjectPool<CommandBuffer>(null, x => x.Clear());
|
||||
|
||||
/// <summary>
|
||||
/// Get a new Command Buffer.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static CommandBuffer Get()
|
||||
{
|
||||
var cmd = s_BufferPool.Get();
|
||||
// Set to empty on purpose, does not create profiling markers.
|
||||
cmd.name = "";
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a new Command Buffer and assign a name to it.
|
||||
/// Named Command Buffers will add profiling makers implicitly for the buffer execution.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public static CommandBuffer Get(string name)
|
||||
{
|
||||
var cmd = s_BufferPool.Get();
|
||||
cmd.name = name;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a Command Buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
public static void Release(CommandBuffer buffer)
|
||||
{
|
||||
s_BufferPool.Release(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bc85748f0cef607499e03b46e3846ebd
|
||||
timeCreated: 1497364325
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Render Textures clear flag.
|
||||
/// This is an legacy alias for RTClearFlags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ClearFlag
|
||||
{
|
||||
/// <summary>Don't clear.</summary>
|
||||
None = RTClearFlags.None,
|
||||
/// <summary>Clear the color buffer.</summary>
|
||||
Color = RTClearFlags.Color,
|
||||
/// <summary>Clear the depth buffer.</summary>
|
||||
Depth = RTClearFlags.Depth,
|
||||
/// <summary>Clear the stencil buffer.</summary>
|
||||
Stencil = RTClearFlags.Stencil,
|
||||
/// <summary>Clear the depth and stencil buffers.</summary>
|
||||
DepthStencil = Depth | Stencil,
|
||||
/// <summary>Clear the color and stencil buffers.</summary>
|
||||
ColorStencil = Color | Stencil,
|
||||
/// <summary>Clear both color, depth and stencil buffers.</summary>
|
||||
All = Color | Depth | Stencil
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: df81bb6ebd9587b4a87f9974080eebf1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
namespace UnityEngine.Rendering
|
||||
{
|
||||
// Use this class to get a static instance of a component
|
||||
// Mainly used to have a default instance
|
||||
|
||||
/// <summary>
|
||||
/// Singleton of a Component class.
|
||||
/// </summary>
|
||||
/// <typeparam name="TType">Component type.</typeparam>
|
||||
public static class ComponentSingleton<TType>
|
||||
where TType : Component
|
||||
{
|
||||
static TType s_Instance = null;
|
||||
/// <summary>
|
||||
/// Instance of the required component type.
|
||||
/// </summary>
|
||||
public static TType instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
{
|
||||
GameObject go = new GameObject("Default " + typeof(TType).Name) { hideFlags = HideFlags.HideAndDontSave };
|
||||
go.SetActive(false);
|
||||
s_Instance = go.AddComponent<TType>();
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the component singleton.
|
||||
/// </summary>
|
||||
public static void Release()
|
||||
{
|
||||
if (s_Instance != null)
|
||||
{
|
||||
var go = s_Instance.gameObject;
|
||||
CoreUtils.Destroy(go);
|
||||
s_Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f78c611171f4b4946b5cf8394378931f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,391 @@
|
|||
using System.Collections.Generic;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant Buffer management class.
|
||||
/// </summary>
|
||||
public class ConstantBuffer
|
||||
{
|
||||
static List<ConstantBufferBase> m_RegisteredConstantBuffers = new List<ConstantBufferBase>();
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it globally via a command buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void PushGlobal<CBType>(CommandBuffer cmd, in CBType data, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(cmd, data);
|
||||
cb.SetGlobal(cmd, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it globally.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void PushGlobal<CBType>(in CBType data, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(data);
|
||||
cb.SetGlobal(shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it to a compute shader via a command buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void Push<CBType>(CommandBuffer cmd, in CBType data, ComputeShader cs, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(cmd, data);
|
||||
cb.Set(cmd, cs, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it to a compute shader.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void Push<CBType>(in CBType data, ComputeShader cs, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(data);
|
||||
cb.Set(cs, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it to a material via a command buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="mat">Material to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void Push<CBType>(CommandBuffer cmd, in CBType data, Material mat, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(cmd, data);
|
||||
cb.Set(mat, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it to a material.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="mat">Material to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void Push<CBType>(in CBType data, Material mat, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(data);
|
||||
cb.Set(mat, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer via a command buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
public static void UpdateData<CBType>(CommandBuffer cmd, in CBType data) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(cmd, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
public static void UpdateData<CBType>(in CBType data) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.UpdateData(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer globally via a command buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void SetGlobal<CBType>(CommandBuffer cmd, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.SetGlobal(cmd, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer globally.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void SetGlobal<CBType>(int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.SetGlobal(shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer to a compute shader via a command buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void Set<CBType>(CommandBuffer cmd, ComputeShader cs, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.Set(cmd, cs, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer to a compute shader.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void Set<CBType>(ComputeShader cs, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.Set(cs, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer to a material.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
/// <param name="mat">Material to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public static void Set<CBType>(Material mat, int shaderId) where CBType : struct
|
||||
{
|
||||
var cb = ConstantBufferSingleton<CBType>.instance;
|
||||
|
||||
cb.Set(mat, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release all currently allocated singleton constant buffers.
|
||||
/// This needs to be called before shutting down the application.
|
||||
/// </summary>
|
||||
public static void ReleaseAll()
|
||||
{
|
||||
foreach (var cb in m_RegisteredConstantBuffers)
|
||||
cb.Release();
|
||||
|
||||
m_RegisteredConstantBuffers.Clear();
|
||||
}
|
||||
|
||||
internal static void Register(ConstantBufferBase cb)
|
||||
{
|
||||
m_RegisteredConstantBuffers.Add(cb);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base class of Constant Buffer.
|
||||
/// </summary>
|
||||
public abstract class ConstantBufferBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Release the constant buffer.
|
||||
/// </summary>
|
||||
public abstract void Release();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An instance of a constant buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
|
||||
public class ConstantBuffer<CBType> : ConstantBufferBase where CBType : struct
|
||||
{
|
||||
// Used to track all global bindings used by this CB type.
|
||||
HashSet<int> m_GlobalBindings = new HashSet<int>();
|
||||
// Array is required by the ComputeBuffer SetData API
|
||||
CBType[] m_Data = new CBType[1];
|
||||
|
||||
|
||||
ComputeBuffer m_GPUConstantBuffer = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constant Buffer constructor.
|
||||
/// </summary>
|
||||
public ConstantBuffer()
|
||||
{
|
||||
m_GPUConstantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf<CBType>(), ComputeBufferType.Constant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer via a command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
public void UpdateData(CommandBuffer cmd, in CBType data)
|
||||
{
|
||||
m_Data[0] = data;
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
cmd.SetBufferData(m_GPUConstantBuffer, m_Data);
|
||||
#else
|
||||
cmd.SetComputeBufferData(m_GPUConstantBuffer, m_Data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
public void UpdateData(in CBType data)
|
||||
{
|
||||
m_Data[0] = data;
|
||||
m_GPUConstantBuffer.SetData(m_Data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer globally via a command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public void SetGlobal(CommandBuffer cmd, int shaderId)
|
||||
{
|
||||
m_GlobalBindings.Add(shaderId);
|
||||
cmd.SetGlobalConstantBuffer(m_GPUConstantBuffer, shaderId, 0, m_GPUConstantBuffer.stride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer globally.
|
||||
/// </summary>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public void SetGlobal(int shaderId)
|
||||
{
|
||||
m_GlobalBindings.Add(shaderId);
|
||||
Shader.SetGlobalConstantBuffer(shaderId, m_GPUConstantBuffer, 0, m_GPUConstantBuffer.stride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer to a compute shader via a command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public void Set(CommandBuffer cmd, ComputeShader cs, int shaderId)
|
||||
{
|
||||
cmd.SetComputeConstantBufferParam(cs, shaderId, m_GPUConstantBuffer, 0, m_GPUConstantBuffer.stride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer to a compute shader.
|
||||
/// </summary>
|
||||
/// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public void Set(ComputeShader cs, int shaderId)
|
||||
{
|
||||
cs.SetConstantBuffer(shaderId, m_GPUConstantBuffer, 0, m_GPUConstantBuffer.stride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind the constant buffer to a material.
|
||||
/// </summary>
|
||||
/// <param name="mat">Material to which the constant buffer should be bound.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public void Set(Material mat, int shaderId)
|
||||
{
|
||||
// This isn't done via command buffer because as long as the buffer itself is not destroyed,
|
||||
// the binding stays valid. Only the commit of data needs to go through the command buffer.
|
||||
// We do it here anyway for now to simplify user API.
|
||||
mat.SetConstantBuffer(shaderId, m_GPUConstantBuffer, 0, m_GPUConstantBuffer.stride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it globally via a command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public void PushGlobal(CommandBuffer cmd, in CBType data, int shaderId)
|
||||
{
|
||||
UpdateData(cmd, data);
|
||||
SetGlobal(cmd, shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the GPU data of the constant buffer and bind it globally.
|
||||
/// </summary>
|
||||
/// <param name="data">Input data of the constant buffer.</param>
|
||||
/// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
|
||||
public void PushGlobal(in CBType data, int shaderId)
|
||||
{
|
||||
UpdateData(data);
|
||||
SetGlobal(shaderId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the constant buffers.
|
||||
/// </summary>
|
||||
public override void Release()
|
||||
{
|
||||
// Depending on the device, globally bound buffers can leave stale "valid" shader ids pointing to a destroyed buffer.
|
||||
// In DX11 it does not cause issues but on Vulkan this will result in skipped drawcalls (even if the buffer is not actually accessed in the shader).
|
||||
// To avoid this kind of issues, it's good practice to "unbind" all globally bound buffers upon destruction.
|
||||
foreach (int shaderId in m_GlobalBindings)
|
||||
Shader.SetGlobalConstantBuffer(shaderId, (ComputeBuffer)null, 0, 0);
|
||||
m_GlobalBindings.Clear();
|
||||
|
||||
CoreUtils.SafeRelease(m_GPUConstantBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
class ConstantBufferSingleton<CBType> : ConstantBuffer<CBType> where CBType : struct
|
||||
{
|
||||
static ConstantBufferSingleton<CBType> s_Instance = null;
|
||||
internal static ConstantBufferSingleton<CBType> instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
{
|
||||
s_Instance = new ConstantBufferSingleton<CBType>();
|
||||
ConstantBuffer.Register(s_Instance);
|
||||
}
|
||||
return s_Instance;
|
||||
}
|
||||
set
|
||||
{
|
||||
s_Instance = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Release()
|
||||
{
|
||||
base.Release();
|
||||
s_Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 61649e120aa78c04e8b44d1c5af17d35
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute used to customize UI display.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class DisplayInfoAttribute : Attribute
|
||||
{
|
||||
/// <summary>Display name used in UI.</summary>
|
||||
public string name;
|
||||
/// <summary>Display order used in UI.</summary>
|
||||
public int order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute used to customize UI display to allow properties only be visible when "Show Additional Properties" is selected
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class AdditionalPropertyAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07346c0b2cba0214f8f27c46ec2dd613
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace UnityEngine.Rendering
|
||||
{
|
||||
internal enum CoreProfileId
|
||||
{
|
||||
BlitTextureInPotAtlas,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: da3d290bbfba048588cdeed618e4ad64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,716 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class with unsafe utility functions.
|
||||
/// </summary>
|
||||
public static unsafe class CoreUnsafeUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed Buffer String Queue class.
|
||||
/// </summary>
|
||||
public struct FixedBufferStringQueue
|
||||
{
|
||||
byte* m_ReadCursor;
|
||||
byte* m_WriteCursor;
|
||||
|
||||
readonly byte* m_BufferEnd;
|
||||
readonly byte* m_BufferStart;
|
||||
readonly int m_BufferLength;
|
||||
|
||||
/// <summary>
|
||||
/// Number of element in the queue.
|
||||
/// </summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="ptr">Buffer pointer.</param>
|
||||
/// <param name="length">Length of the provided allocated buffer in byte.</param>
|
||||
public FixedBufferStringQueue(byte* ptr, int length)
|
||||
{
|
||||
m_BufferStart = ptr;
|
||||
m_BufferLength = length;
|
||||
|
||||
m_BufferEnd = m_BufferStart + m_BufferLength;
|
||||
m_ReadCursor = m_BufferStart;
|
||||
m_WriteCursor = m_BufferStart;
|
||||
Count = 0;
|
||||
Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to push a new element in the queue.
|
||||
/// </summary>
|
||||
/// <param name="v">Element to push in the queue.</param>
|
||||
/// <returns>True if the new element could be pushed in the queue. False if reserved memory was not enough.</returns>
|
||||
public bool TryPush(string v)
|
||||
{
|
||||
var size = v.Length * sizeof(char) + sizeof(int);
|
||||
if (m_WriteCursor + size >= m_BufferEnd)
|
||||
return false;
|
||||
|
||||
*(int*)m_WriteCursor = v.Length;
|
||||
m_WriteCursor += sizeof(int);
|
||||
|
||||
var charPtr = (char*)m_WriteCursor;
|
||||
for (int i = 0; i < v.Length; ++i, ++charPtr)
|
||||
*charPtr = v[i];
|
||||
|
||||
m_WriteCursor += sizeof(char) * v.Length;
|
||||
++Count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop an element of the queue.
|
||||
/// </summary>
|
||||
/// <param name="v">Output result string.</param>
|
||||
/// <returns>True if an element was succesfuly poped.</returns>
|
||||
public bool TryPop(out string v)
|
||||
{
|
||||
var size = *(int*)m_ReadCursor;
|
||||
if (size != 0)
|
||||
{
|
||||
m_ReadCursor += sizeof(int);
|
||||
v = new string((char*)m_ReadCursor, 0, size);
|
||||
m_ReadCursor += size * sizeof(char);
|
||||
return true;
|
||||
}
|
||||
|
||||
v = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the queue.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_WriteCursor = m_BufferStart;
|
||||
m_ReadCursor = m_BufferStart;
|
||||
Count = 0;
|
||||
UnsafeUtility.MemClear(m_BufferStart, m_BufferLength);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Key Getter interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">Value</typeparam>
|
||||
/// <typeparam name="TKey">Key</typeparam>
|
||||
public interface IKeyGetter<TValue, TKey>
|
||||
{
|
||||
/// <summary>Getter</summary>
|
||||
/// <param name="v">The value</param>
|
||||
/// <returns>The key</returns>
|
||||
TKey Get(ref TValue v);
|
||||
}
|
||||
|
||||
internal struct DefaultKeyGetter<T> : IKeyGetter<T, T>
|
||||
{ public T Get(ref T v) { return v; } }
|
||||
|
||||
// Note: this is a workaround needed to circumvent some AOT issues when building for xbox
|
||||
internal struct UintKeyGetter : IKeyGetter<uint, uint>
|
||||
{ public uint Get(ref uint v) { return v; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extension method to copy elements of a list into a buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the provided List.</typeparam>
|
||||
/// <param name="list">Input List.</param>
|
||||
/// <param name="dest">Destination buffer.</param>
|
||||
/// <param name="count">Number of elements to copy.</param>
|
||||
public static void CopyTo<T>(this List<T> list, void* dest, int count)
|
||||
where T : struct
|
||||
{
|
||||
var c = Mathf.Min(count, list.Count);
|
||||
for (int i = 0; i < c; ++i)
|
||||
UnsafeUtility.WriteArrayElement<T>(dest, i, list[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method to copy elements of an array into a buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the provided array.</typeparam>
|
||||
/// <param name="list">Input List.</param>
|
||||
/// <param name="dest">Destination buffer.</param>
|
||||
/// <param name="count">Number of elements to copy.</param>
|
||||
public static void CopyTo<T>(this T[] list, void* dest, int count)
|
||||
where T : struct
|
||||
{
|
||||
var c = Mathf.Min(count, list.Length);
|
||||
for (int i = 0; i < c; ++i)
|
||||
UnsafeUtility.WriteArrayElement<T>(dest, i, list[i]);
|
||||
}
|
||||
|
||||
private static void CalculateRadixParams(int radixBits, out int bitStates)
|
||||
{
|
||||
if (radixBits != 2 && radixBits != 4 && radixBits != 8)
|
||||
throw new Exception("Radix bits must be 2, 4 or 8 for uint radix sort.");
|
||||
bitStates = 1 << radixBits;
|
||||
}
|
||||
|
||||
private static int CalculateRadixSupportSize(int bitStates, int arrayLength)
|
||||
{
|
||||
return bitStates * 3 + arrayLength;
|
||||
}
|
||||
|
||||
private static unsafe void CalculateRadixSortSupportArrays(
|
||||
int bitStates, int arrayLength, uint* supportArray,
|
||||
out uint* bucketIndices, out uint* bucketSizes, out uint* bucketPrefix, out uint* arrayOutput)
|
||||
{
|
||||
bucketIndices = supportArray;
|
||||
bucketSizes = bucketIndices + bitStates;
|
||||
bucketPrefix = bucketSizes + bitStates;
|
||||
arrayOutput = bucketPrefix + bitStates;
|
||||
}
|
||||
|
||||
private static unsafe void MergeSort(uint* array, uint* support, int length)
|
||||
{
|
||||
for (int k = 1; k < length; k *= 2)
|
||||
{
|
||||
for (int left = 0; left + k < length; left += k * 2)
|
||||
{
|
||||
int right = left + k;
|
||||
int rightend = right + k;
|
||||
if (rightend > length)
|
||||
rightend = length;
|
||||
int m = left;
|
||||
int i = left;
|
||||
int j = right;
|
||||
while (i < right && j < rightend)
|
||||
{
|
||||
if (array[i] <= array[j])
|
||||
{
|
||||
support[m] = array[i++];
|
||||
}
|
||||
else
|
||||
{
|
||||
support[m] = array[j++];
|
||||
}
|
||||
m++;
|
||||
}
|
||||
while (i < right)
|
||||
{
|
||||
support[m] = array[i++];
|
||||
m++;
|
||||
}
|
||||
while (j < rightend)
|
||||
{
|
||||
support[m] = array[j++];
|
||||
m++;
|
||||
}
|
||||
for (m = left; m < rightend; m++)
|
||||
{
|
||||
array[m] = support[m];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge sort - non recursive
|
||||
/// </summary>
|
||||
/// <param name="arr">Array to sort.</param>
|
||||
/// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param>
|
||||
/// <param name="supportArray">Secondary array reference, used to store intermediate merge results.</param>
|
||||
public static unsafe void MergeSort(uint[] arr, int sortSize, ref uint[] supportArray)
|
||||
{
|
||||
sortSize = Math.Min(sortSize, arr.Length);
|
||||
if (arr == null || sortSize == 0)
|
||||
return;
|
||||
|
||||
if (supportArray == null || supportArray.Length < sortSize)
|
||||
supportArray = new uint[sortSize];
|
||||
|
||||
fixed (uint* arrPtr = arr)
|
||||
fixed (uint* supportPtr = supportArray)
|
||||
CoreUnsafeUtils.MergeSort(arrPtr, supportPtr, sortSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge sort - non recursive
|
||||
/// </summary>
|
||||
/// <param name="arr">Array to sort.</param>
|
||||
/// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param>
|
||||
/// <param name="supportArray">Secondary array reference, used to store intermediate merge results.</param>
|
||||
public static unsafe void MergeSort(NativeArray<uint> arr, int sortSize, ref NativeArray<uint> supportArray)
|
||||
{
|
||||
sortSize = Math.Min(sortSize, arr.Length);
|
||||
if (!arr.IsCreated || sortSize == 0)
|
||||
return;
|
||||
|
||||
if (!supportArray.IsCreated || supportArray.Length < sortSize)
|
||||
supportArray.ResizeArray(arr.Length);
|
||||
|
||||
CoreUnsafeUtils.MergeSort((uint*)arr.GetUnsafePtr(), (uint*)supportArray.GetUnsafePtr(), sortSize);
|
||||
}
|
||||
|
||||
private static unsafe void InsertionSort(uint* arr, int length)
|
||||
{
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
for (int j = i; j >= 1; --j)
|
||||
{
|
||||
if (arr[j] >= arr[j - 1])
|
||||
break;
|
||||
|
||||
var tmp = arr[j];
|
||||
arr[j] = arr[j - 1];
|
||||
arr[j - 1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insertion sort
|
||||
/// </summary>
|
||||
/// <param name="arr">Array to sort.</param>
|
||||
/// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param>
|
||||
public static unsafe void InsertionSort(uint[] arr, int sortSize)
|
||||
{
|
||||
sortSize = Math.Min(arr.Length, sortSize);
|
||||
if (arr == null || sortSize == 0)
|
||||
return;
|
||||
|
||||
fixed (uint* ptr = arr)
|
||||
CoreUnsafeUtils.InsertionSort(ptr, sortSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insertion sort
|
||||
/// </summary>
|
||||
/// <param name="arr">Array to sort.</param>
|
||||
/// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param>
|
||||
public static unsafe void InsertionSort(NativeArray<uint> arr, int sortSize)
|
||||
{
|
||||
sortSize = Math.Min(arr.Length, sortSize);
|
||||
if (!arr.IsCreated || sortSize == 0)
|
||||
return;
|
||||
|
||||
CoreUnsafeUtils.InsertionSort((uint*)arr.GetUnsafePtr(), sortSize);
|
||||
}
|
||||
|
||||
private static unsafe void RadixSort(uint* array, uint* support, int radixBits, int bitStates, int length)
|
||||
{
|
||||
uint mask = (uint)(bitStates - 1);
|
||||
CalculateRadixSortSupportArrays(bitStates, length, support, out uint* bucketIndices, out uint* bucketSizes, out uint* bucketPrefix, out uint* arrayOutput);
|
||||
|
||||
int buckets = (sizeof(uint) * 8) / radixBits;
|
||||
uint* targetBuffer = arrayOutput;
|
||||
uint* inputBuffer = array;
|
||||
for (int b = 0; b < buckets; ++b)
|
||||
{
|
||||
int shift = b * radixBits;
|
||||
for (int s = 0; s < 3 * bitStates; ++s)
|
||||
bucketIndices[s] = 0;//bucketSizes and bucketPrefix get zeroed, since we walk 3x the bit states
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
bucketSizes[((inputBuffer[i] >> shift) & mask)]++;
|
||||
|
||||
for (int s = 1; s < bitStates; ++s)
|
||||
bucketPrefix[s] = bucketPrefix[s - 1] + bucketSizes[s - 1];
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
uint val = inputBuffer[i];
|
||||
uint bucket = (val >> shift) & mask;
|
||||
targetBuffer[bucketPrefix[bucket] + bucketIndices[bucket]++] = val;
|
||||
}
|
||||
|
||||
uint* tmp = inputBuffer;
|
||||
inputBuffer = targetBuffer;
|
||||
targetBuffer = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Radix Sort
|
||||
/// Radix sort or bucket sort, stable and non in place.
|
||||
/// </summary>
|
||||
/// <param name="arr">Array to sort.</param>
|
||||
/// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param>
|
||||
/// <param name="supportArray">Array of uints that is used for support data. The algorithm will automatically allocate it if necessary.</param>
|
||||
/// <param name="radixBits">Number of bits to use for each bucket. Can only be 8, 4 or 2.</param>
|
||||
public static unsafe void RadixSort(uint[] arr, int sortSize, ref uint[] supportArray, int radixBits = 8)
|
||||
{
|
||||
sortSize = Math.Min(sortSize, arr.Length);
|
||||
CalculateRadixParams(radixBits, out int bitStates);
|
||||
if (arr == null || sortSize == 0)
|
||||
return;
|
||||
|
||||
int supportSize = CalculateRadixSupportSize(bitStates, sortSize);
|
||||
if (supportArray == null || supportArray.Length < supportSize)
|
||||
supportArray = new uint[supportSize];
|
||||
|
||||
fixed (uint* ptr = arr)
|
||||
fixed (uint* supportArrayPtr = supportArray)
|
||||
CoreUnsafeUtils.RadixSort(ptr, supportArrayPtr, radixBits, bitStates, sortSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Radix Sort
|
||||
/// Radix sort or bucket sort, stable and non in place.
|
||||
/// </summary>
|
||||
/// <param name="arr">Array to sort.</param>
|
||||
/// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param>
|
||||
/// <param name="supportArray">Array of uints that is used for support data. The algorithm will automatically allocate it if necessary.</param>
|
||||
/// <param name="radixBits">Number of bits to use for each bucket. Can only be 8, 4 or 2.</param>
|
||||
public static unsafe void RadixSort(NativeArray<uint> array, int sortSize, ref NativeArray<uint> supportArray, int radixBits = 8)
|
||||
{
|
||||
sortSize = Math.Min(sortSize, array.Length);
|
||||
CalculateRadixParams(radixBits, out int bitStates);
|
||||
if (!array.IsCreated || sortSize == 0)
|
||||
return;
|
||||
|
||||
int supportSize = CalculateRadixSupportSize(bitStates, sortSize);
|
||||
if (!supportArray.IsCreated || supportArray.Length < supportSize)
|
||||
supportArray.ResizeArray((int)supportSize);
|
||||
|
||||
CoreUnsafeUtils.RadixSort((uint*)array.GetUnsafePtr(), (uint*)supportArray.GetUnsafePtr(), radixBits, bitStates, sortSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quick Sort
|
||||
/// </summary>
|
||||
/// <param name="arr">uint array.</param>
|
||||
/// <param name="left">Left boundary.</param>
|
||||
/// <param name="right">Left boundary.</param>
|
||||
public static unsafe void QuickSort(uint[] arr, int left, int right)
|
||||
{
|
||||
fixed (uint* ptr = arr)
|
||||
CoreUnsafeUtils.QuickSort<uint, uint, UintKeyGetter>(ptr, left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quick sort.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to compare.</typeparam>
|
||||
/// <param name="count">Number of element.</param>
|
||||
/// <param name="data">Buffer to sort.</param>
|
||||
public static void QuickSort<T>(int count, void* data)
|
||||
where T : struct, IComparable<T>
|
||||
{
|
||||
QuickSort<T, T, DefaultKeyGetter<T>>(data, 0, count - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quick sort.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">Value type.</typeparam>
|
||||
/// <typeparam name="TKey">Key Type.</typeparam>
|
||||
/// <typeparam name="TGetter">Getter type.</typeparam>
|
||||
/// <param name="count">Number of element.</param>
|
||||
/// <param name="data">Data to sort.</param>
|
||||
public static void QuickSort<TValue, TKey, TGetter>(int count, void* data)
|
||||
where TKey : struct, IComparable<TKey>
|
||||
where TValue : struct
|
||||
where TGetter : struct, IKeyGetter<TValue, TKey>
|
||||
{
|
||||
QuickSort<TValue, TKey, TGetter>(data, 0, count - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quick sort.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">Value type.</typeparam>
|
||||
/// <typeparam name="TKey">Key Type.</typeparam>
|
||||
/// <typeparam name="TGetter">Getter type.</typeparam>
|
||||
/// <param name="data">Data to sort.</param>
|
||||
/// <param name="left">Left boundary.</param>
|
||||
/// <param name="right">Right boundary.</param>
|
||||
public static void QuickSort<TValue, TKey, TGetter>(void* data, int left, int right)
|
||||
where TKey : struct, IComparable<TKey>
|
||||
where TValue : struct
|
||||
where TGetter : struct, IKeyGetter<TValue, TKey>
|
||||
{
|
||||
// For Recursion
|
||||
if (left < right)
|
||||
{
|
||||
int pivot = Partition<TValue, TKey, TGetter>(data, left, right);
|
||||
|
||||
if (pivot >= 1)
|
||||
QuickSort<TValue, TKey, TGetter>(data, left, pivot);
|
||||
|
||||
if (pivot + 1 < right)
|
||||
QuickSort<TValue, TKey, TGetter>(data, pivot + 1, right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index of an element in a buffer.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Data type.</typeparam>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="count">Number of elements.</param>
|
||||
/// <param name="v">Element to test against.</param>
|
||||
/// <returns>The first index of the provided element.</returns>
|
||||
public static int IndexOf<T>(void* data, int count, T v)
|
||||
where T : struct, IEquatable<T>
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
if (UnsafeUtility.ReadArrayElement<T>(data, i).Equals(v))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare hashes of two collections and provide
|
||||
/// a list of indices <paramref name="removeIndices"/> to remove in <paramref name="oldHashes"/>
|
||||
/// and a list of indices <paramref name="addIndices"/> to add in <paramref name="newHashes"/>.
|
||||
///
|
||||
/// Assumes that <paramref name="newHashes"/> and <paramref name="oldHashes"/> are sorted.
|
||||
/// </summary>
|
||||
/// <typeparam name="TOldValue">Old value type.</typeparam>
|
||||
/// <typeparam name="TOldGetter">Old getter type.</typeparam>
|
||||
/// <typeparam name="TNewValue">New value type.</typeparam>
|
||||
/// <typeparam name="TNewGetter">New getter type.</typeparam>
|
||||
/// <param name="oldHashCount">Number of hashes in <paramref name="oldHashes"/>.</param>
|
||||
/// <param name="oldHashes">Previous hashes to compare.</param>
|
||||
/// <param name="newHashCount">Number of hashes in <paramref name="newHashes"/>.</param>
|
||||
/// <param name="newHashes">New hashes to compare.</param>
|
||||
/// <param name="addIndices">Indices of element to add in <paramref name="newHashes"/> will be written here.</param>
|
||||
/// <param name="removeIndices">Indices of element to remove in <paramref name="oldHashes"/> will be written here.</param>
|
||||
/// <param name="addCount">Number of elements to add will be written here.</param>
|
||||
/// <param name="remCount">Number of elements to remove will be written here.</param>
|
||||
/// <returns>The number of operation to perform (<code><paramref name="addCount"/> + <paramref name="remCount"/></code>)</returns>
|
||||
|
||||
public static int CompareHashes<TOldValue, TOldGetter, TNewValue, TNewGetter>(
|
||||
int oldHashCount, void* oldHashes,
|
||||
int newHashCount, void* newHashes,
|
||||
// assume that the capacity of indices is >= max(oldHashCount, newHashCount)
|
||||
int* addIndices, int* removeIndices,
|
||||
out int addCount, out int remCount
|
||||
)
|
||||
where TOldValue : struct
|
||||
where TNewValue : struct
|
||||
where TOldGetter : struct, IKeyGetter<TOldValue, Hash128>
|
||||
where TNewGetter : struct, IKeyGetter<TNewValue, Hash128>
|
||||
{
|
||||
var oldGetter = new TOldGetter();
|
||||
var newGetter = new TNewGetter();
|
||||
|
||||
addCount = 0;
|
||||
remCount = 0;
|
||||
// Check combined hashes
|
||||
if (oldHashCount == newHashCount)
|
||||
{
|
||||
var oldHash = new Hash128();
|
||||
var newHash = new Hash128();
|
||||
CombineHashes<TOldValue, TOldGetter>(oldHashCount, oldHashes, &oldHash);
|
||||
CombineHashes<TNewValue, TNewGetter>(newHashCount, newHashes, &newHash);
|
||||
if (oldHash == newHash)
|
||||
return 0;
|
||||
}
|
||||
|
||||
var numOperations = 0;
|
||||
|
||||
var oldI = 0;
|
||||
var newI = 0;
|
||||
|
||||
while (oldI < oldHashCount || newI < newHashCount)
|
||||
{
|
||||
// At the end of old array.
|
||||
if (oldI == oldHashCount)
|
||||
{
|
||||
// No more hashes in old array. Add remaining entries from new array.
|
||||
for (; newI < newHashCount; ++newI)
|
||||
{
|
||||
addIndices[addCount++] = newI;
|
||||
++numOperations;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// At end of new array.
|
||||
if (newI == newHashCount)
|
||||
{
|
||||
// No more hashes in old array. Remove remaining entries from old array.
|
||||
for (; oldI < oldHashCount; ++oldI)
|
||||
{
|
||||
removeIndices[remCount++] = oldI;
|
||||
++numOperations;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Both arrays have data.
|
||||
var newVal = UnsafeUtility.ReadArrayElement<TNewValue>(newHashes, newI);
|
||||
var oldVal = UnsafeUtility.ReadArrayElement<TOldValue>(oldHashes, oldI);
|
||||
var newKey = newGetter.Get(ref newVal);
|
||||
var oldKey = oldGetter.Get(ref oldVal);
|
||||
if (newKey == oldKey)
|
||||
{
|
||||
// Matching hash, skip.
|
||||
++newI;
|
||||
++oldI;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Both arrays have data, but hashes do not match.
|
||||
if (newKey < oldKey)
|
||||
{
|
||||
// oldIter is the greater hash. Push "add" jobs from the new array until reaching the oldIter hash.
|
||||
while (newI < newHashCount && newKey < oldKey)
|
||||
{
|
||||
addIndices[addCount++] = newI;
|
||||
++newI;
|
||||
++numOperations;
|
||||
newVal = UnsafeUtility.ReadArrayElement<TNewValue>(newHashes, newI);
|
||||
newKey = newGetter.Get(ref newVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// newIter is the greater hash. Push "remove" jobs from the old array until reaching the newIter hash.
|
||||
while (oldI < oldHashCount && oldKey < newKey)
|
||||
{
|
||||
removeIndices[remCount++] = oldI;
|
||||
++numOperations;
|
||||
++oldI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numOperations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare hashes.
|
||||
/// </summary>
|
||||
/// <param name="oldHashCount">Number of hashes in <paramref name="oldHashes"/>.</param>
|
||||
/// <param name="oldHashes">Previous hashes to compare.</param>
|
||||
/// <param name="newHashCount">Number of hashes in <paramref name="newHashes"/>.</param>
|
||||
/// <param name="newHashes">New hashes to compare.</param>
|
||||
/// <param name="addIndices">Indices of element to add in <paramref name="newHashes"/> will be written here.</param>
|
||||
/// <param name="removeIndices">Indices of element to remove in <paramref name="oldHashes"/> will be written here.</param>
|
||||
/// <param name="addCount">Number of elements to add will be written here.</param>
|
||||
/// <param name="remCount">Number of elements to remove will be written here.</param>
|
||||
/// <returns>The number of operation to perform (<code><paramref name="addCount"/> + <paramref name="remCount"/></code>)</returns>
|
||||
public static int CompareHashes(
|
||||
int oldHashCount, Hash128* oldHashes,
|
||||
int newHashCount, Hash128* newHashes,
|
||||
// assume that the capacity of indices is >= max(oldHashCount, newHashCount)
|
||||
int* addIndices, int* removeIndices,
|
||||
out int addCount, out int remCount
|
||||
)
|
||||
{
|
||||
return CompareHashes<Hash128, DefaultKeyGetter<Hash128>, Hash128, DefaultKeyGetter<Hash128>>(
|
||||
oldHashCount, oldHashes,
|
||||
newHashCount, newHashes,
|
||||
addIndices, removeIndices,
|
||||
out addCount, out remCount
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Combine all of the hashes of a collection of hashes.</summary>
|
||||
/// <typeparam name="TValue">Value type.</typeparam>
|
||||
/// <typeparam name="TGetter">Getter type.</typeparam>
|
||||
/// <param name="count">Number of hash to combine.</param>
|
||||
/// <param name="hashes">Hashes to combine.</param>
|
||||
/// <param name="outHash">Hash to update.</param>
|
||||
public static void CombineHashes<TValue, TGetter>(int count, void* hashes, Hash128* outHash)
|
||||
where TValue : struct
|
||||
where TGetter : struct, IKeyGetter<TValue, Hash128>
|
||||
{
|
||||
var getter = new TGetter();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var v = UnsafeUtility.ReadArrayElement<TValue>(hashes, i);
|
||||
var h = getter.Get(ref v);
|
||||
HashUtilities.AppendHash(ref h, ref *outHash);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combine hashes.
|
||||
/// </summary>
|
||||
/// <param name="count">Number of hash to combine.</param>
|
||||
/// <param name="hashes">Hashes to combine.</param>
|
||||
/// <param name="outHash">Hash to update.</param>
|
||||
public static void CombineHashes(int count, Hash128* hashes, Hash128* outHash)
|
||||
{
|
||||
CombineHashes<Hash128, DefaultKeyGetter<Hash128>>(count, hashes, outHash);
|
||||
}
|
||||
|
||||
// Just a sort function that doesn't allocate memory
|
||||
// Note: Should be replace by a radix sort for positive integer
|
||||
static int Partition<TValue, TKey, TGetter>(void* data, int left, int right)
|
||||
where TKey : struct, IComparable<TKey>
|
||||
where TValue : struct
|
||||
where TGetter : struct, IKeyGetter<TValue, TKey>
|
||||
{
|
||||
var getter = default(TGetter);
|
||||
var pivotvalue = UnsafeUtility.ReadArrayElement<TValue>(data, left);
|
||||
var pivot = getter.Get(ref pivotvalue);
|
||||
|
||||
--left;
|
||||
++right;
|
||||
while (true)
|
||||
{
|
||||
var c = 0;
|
||||
var lvalue = default(TValue);
|
||||
var lkey = default(TKey);
|
||||
do
|
||||
{
|
||||
++left;
|
||||
lvalue = UnsafeUtility.ReadArrayElement<TValue>(data, left);
|
||||
lkey = getter.Get(ref lvalue);
|
||||
c = lkey.CompareTo(pivot);
|
||||
}
|
||||
while (c < 0);
|
||||
|
||||
var rvalue = default(TValue);
|
||||
var rkey = default(TKey);
|
||||
do
|
||||
{
|
||||
--right;
|
||||
rvalue = UnsafeUtility.ReadArrayElement<TValue>(data, right);
|
||||
rkey = getter.Get(ref rvalue);
|
||||
c = rkey.CompareTo(pivot);
|
||||
}
|
||||
while (c > 0);
|
||||
|
||||
if (left < right)
|
||||
{
|
||||
UnsafeUtility.WriteArrayElement(data, right, lvalue);
|
||||
UnsafeUtility.WriteArrayElement(data, left, rvalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for duplicates in an array.
|
||||
/// </summary>
|
||||
/// <param name="arr">Input array.</param>
|
||||
/// <returns>True if there is any duplicate in the input array.</returns>
|
||||
public static unsafe bool HaveDuplicates(int[] arr)
|
||||
{
|
||||
int* copy = stackalloc int[arr.Length];
|
||||
arr.CopyTo<int>(copy, arr.Length);
|
||||
QuickSort<int>(arr.Length, copy);
|
||||
for (int i = arr.Length - 1; i > 0; --i)
|
||||
{
|
||||
if (UnsafeUtility.ReadArrayElement<int>(copy, i).CompareTo(UnsafeUtility.ReadArrayElement<int>(copy, i - 1)) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 69a7aaed92ae1b0469d9b82fba45bcec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic growable array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the array.</typeparam>
|
||||
public class DynamicArray<T> where T : new()
|
||||
{
|
||||
T[] m_Array = null;
|
||||
|
||||
/// <summary>
|
||||
/// Number of elements in the array.
|
||||
/// </summary>
|
||||
public int size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allocated size of the array.
|
||||
/// </summary>
|
||||
public int capacity { get { return m_Array.Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// Defaults to a size of 32 elements.
|
||||
/// </summary>
|
||||
public DynamicArray()
|
||||
{
|
||||
m_Array = new T[32];
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="size">Number of elements.</param>
|
||||
public DynamicArray(int size)
|
||||
{
|
||||
m_Array = new T[size];
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the array of all elements.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the DynamicArray contains a specific value.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to locate in the DynamicArray.</param>
|
||||
/// <returns>true if item is found in the DynamicArray; otherwise, false.</returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return IndexOf(item) != -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an element to the array.
|
||||
/// </summary>
|
||||
/// <param name="value">Element to add to the array.</param>
|
||||
/// <returns>The index of the element.</returns>
|
||||
public int Add(in T value)
|
||||
{
|
||||
int index = size;
|
||||
|
||||
// Grow array if needed;
|
||||
if (index >= m_Array.Length)
|
||||
{
|
||||
var newArray = new T[m_Array.Length * 2];
|
||||
Array.Copy(m_Array, newArray, m_Array.Length);
|
||||
m_Array = newArray;
|
||||
}
|
||||
|
||||
m_Array[index] = value;
|
||||
size++;
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the elements of the specified collection to the end of the DynamicArray.
|
||||
/// </summary>
|
||||
/// <param name="array">The array whose elements should be added to the end of the DynamicArray. The array itself cannot be null, but it can contain elements that are null, if type T is a reference type.</param>
|
||||
public void AddRange(DynamicArray<T> array)
|
||||
{
|
||||
Reserve(size + array.size, true);
|
||||
for (int i = 0; i < array.size; ++i)
|
||||
m_Array[size++] = array[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of a specific object from the DynamicArray.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to remove from the DynamicArray. The value can be null for reference types.</param>
|
||||
/// <returns>true if item is successfully removed; otherwise, false. This method also returns false if item was not found in the DynamicArray.</returns>
|
||||
public bool Remove(T item)
|
||||
{
|
||||
int index = IndexOf(item);
|
||||
if (index != -1)
|
||||
{
|
||||
RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element at the specified index of the DynamicArray.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the element to remove.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= size)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
if (index != size - 1)
|
||||
Array.Copy(m_Array, index + 1, m_Array, index, size - index - 1);
|
||||
|
||||
size--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a range of elements from the DynamicArray.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based starting index of the range of elements to remove.</param>
|
||||
/// <param name="count">The number of elements to remove.</param>
|
||||
public void RemoveRange(int index, int count)
|
||||
{
|
||||
if (index < 0 || index >= size || count < 0 || index + count > size)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
Array.Copy(m_Array, index + count, m_Array, index, size - index - count);
|
||||
size -= count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the DynamicArray that starts at the specified index and contains the specified number of elements.
|
||||
/// </summary>
|
||||
/// <param name="startIndex">The zero-based starting index of the search.</param>
|
||||
/// <param name="count">The number of elements in the section to search.</param>
|
||||
/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
|
||||
/// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, -1.</returns>
|
||||
public int FindIndex(int startIndex, int count, Predicate<T> match)
|
||||
{
|
||||
for (int i = startIndex; i < size; ++i)
|
||||
{
|
||||
if (match(m_Array[i]))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the DynamicArray that starts at the specified index and contains the specified number of elements.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to locate in the DynamicArray. The value can be null for reference types.</param>
|
||||
/// <param name="index">The zero-based starting index of the search. 0 (zero) is valid in an empty list.</param>
|
||||
/// <param name="count">The number of elements in the section to search.</param>
|
||||
/// <returns></returns>
|
||||
public int IndexOf(T item, int index, int count)
|
||||
{
|
||||
for (int i = index; i < size && count > 0; ++i, --count)
|
||||
{
|
||||
if (m_Array[i].Equals(item))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the DynamicArray that extends from the specified index to the last element.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to locate in the DynamicArray. The value can be null for reference types.</param>
|
||||
/// <param name="index">The zero-based starting index of the search. 0 (zero) is valid in an empty list.</param>
|
||||
/// <returns>The zero-based index of the first occurrence of item within the range of elements in the DynamicArray that extends from index to the last element, if found; otherwise, -1.</returns>
|
||||
public int IndexOf(T item, int index)
|
||||
{
|
||||
for (int i = index; i < size; ++i)
|
||||
{
|
||||
if (m_Array[i].Equals(item))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for the specified object and returns the zero-based index of the first occurrence within the entire DynamicArray.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to locate in the DynamicArray. The value can be null for reference types.</param>
|
||||
/// <returns>he zero-based index of the first occurrence of item within the entire DynamicArray, if found; otherwise, -1.</returns>
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
return IndexOf(item, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize the Dynamic Array.
|
||||
/// This will reallocate memory if necessary and set the current size of the array to the provided size.
|
||||
/// </summary>
|
||||
/// <param name="newSize">New size for the array.</param>
|
||||
/// <param name="keepContent">Set to true if you want the current content of the array to be kept.</param>
|
||||
public void Resize(int newSize, bool keepContent = false)
|
||||
{
|
||||
Reserve(newSize, keepContent);
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the total number of elements the internal data structure can hold without resizing.
|
||||
/// </summary>
|
||||
/// <param name="newCapacity">New capacity for the array.</param>
|
||||
/// <param name="keepContent">Set to true if you want the current content of the array to be kept.</param>
|
||||
public void Reserve(int newCapacity, bool keepContent = false)
|
||||
{
|
||||
if (newCapacity > m_Array.Length)
|
||||
{
|
||||
if (keepContent)
|
||||
{
|
||||
var newArray = new T[newCapacity];
|
||||
Array.Copy(m_Array, newArray, m_Array.Length);
|
||||
m_Array = newArray;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Array = new T[newCapacity];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ref access to an element.
|
||||
/// </summary>
|
||||
/// <param name="index">Element index</param>
|
||||
/// <returns>The requested element.</returns>
|
||||
public ref T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
if (index >= size)
|
||||
throw new IndexOutOfRangeException();
|
||||
#endif
|
||||
return ref m_Array[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to regular array.
|
||||
/// </summary>
|
||||
/// <param name="array">Input DynamicArray.</param>
|
||||
/// <returns>The internal array.</returns>
|
||||
public static implicit operator T[](DynamicArray<T> array) => array.m_Array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension class for DynamicArray
|
||||
/// </summary>
|
||||
public static class DynamicArrayExtensions
|
||||
{
|
||||
static int Partition<T>(T[] data, int left, int right) where T : IComparable<T>, new()
|
||||
{
|
||||
var pivot = data[left];
|
||||
|
||||
--left;
|
||||
++right;
|
||||
while (true)
|
||||
{
|
||||
var c = 0;
|
||||
var lvalue = default(T);
|
||||
do
|
||||
{
|
||||
++left;
|
||||
lvalue = data[left];
|
||||
c = lvalue.CompareTo(pivot);
|
||||
}
|
||||
while (c < 0);
|
||||
|
||||
var rvalue = default(T);
|
||||
do
|
||||
{
|
||||
--right;
|
||||
rvalue = data[right];
|
||||
c = rvalue.CompareTo(pivot);
|
||||
}
|
||||
while (c > 0);
|
||||
|
||||
if (left < right)
|
||||
{
|
||||
data[right] = lvalue;
|
||||
data[left] = rvalue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void QuickSort<T>(T[] data, int left, int right) where T : IComparable<T>, new()
|
||||
{
|
||||
if (left < right)
|
||||
{
|
||||
int pivot = Partition(data, left, right);
|
||||
|
||||
if (pivot >= 1)
|
||||
QuickSort(data, left, pivot);
|
||||
|
||||
if (pivot + 1 < right)
|
||||
QuickSort(data, pivot + 1, right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a quick sort on the DynamicArray
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the array.</typeparam>
|
||||
/// <param name="array">Array on which to perform the quick sort.</param>
|
||||
public static void QuickSort<T>(this DynamicArray<T> array) where T : IComparable<T>, new()
|
||||
{
|
||||
QuickSort<T>(array, 0, array.size - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4cce54895670bb4894d26f27e71ba26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,603 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// The format of the delegate used to perofrm dynamic resolution.
|
||||
/// </summary>
|
||||
public delegate float PerformDynamicRes();
|
||||
|
||||
/// <summary>
|
||||
/// The type of dynamic resolution scaler. It essentially defines what the output of the scaler is expected to be.
|
||||
/// </summary>
|
||||
public enum DynamicResScalePolicyType
|
||||
{
|
||||
/// <summary>
|
||||
/// If is the option, DynamicResolutionHandler expects the scaler to return a screen percentage.
|
||||
/// The value set will be clamped between the minimum and maximum percentage set in the GlobalDynamicResolutionSettings.
|
||||
/// </summary>
|
||||
ReturnsPercentage,
|
||||
/// <summary>
|
||||
/// If is the option, DynamicResolutionHandler expects the scaler to return a factor t in the [0..1] such that the final resolution percentage
|
||||
/// is determined by lerp(minimumPercentage, maximumPercentage, t), where the minimum and maximum percentages are the one set in the GlobalDynamicResolutionSettings.
|
||||
/// </summary>
|
||||
ReturnsMinMaxLerpFactor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The source slots for dynamic resolution scaler. Defines registers were the scalers assigned are stored. By default the User one is always used
|
||||
/// </summary>
|
||||
public enum DynamicResScalerSlot
|
||||
{
|
||||
/// <summary> Scaler slot set by the function SetDynamicResScaler</summary>
|
||||
User,
|
||||
/// <summary> Scaler slot set by the function SetSystemDynamicResScaler</summary>
|
||||
System,
|
||||
/// <summary> total number of scaler slots </summary>
|
||||
Count
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class responsible to handle dynamic resolution.
|
||||
/// </summary>
|
||||
public class DynamicResolutionHandler
|
||||
{
|
||||
private bool m_Enabled;
|
||||
private bool m_UseMipBias;
|
||||
private float m_MinScreenFraction;
|
||||
private float m_MaxScreenFraction;
|
||||
private float m_CurrentFraction;
|
||||
private bool m_ForcingRes;
|
||||
private bool m_CurrentCameraRequest;
|
||||
private float m_PrevFraction;
|
||||
private bool m_ForceSoftwareFallback;
|
||||
private bool m_RunUpscalerFilterOnFullResolution;
|
||||
|
||||
private float m_PrevHWScaleWidth;
|
||||
private float m_PrevHWScaleHeight;
|
||||
private Vector2Int m_LastScaledSize;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
m_Enabled = false;
|
||||
m_UseMipBias = false;
|
||||
m_MinScreenFraction = 1.0f;
|
||||
m_MaxScreenFraction = 1.0f;
|
||||
m_CurrentFraction = 1.0f;
|
||||
m_ForcingRes = false;
|
||||
m_CurrentCameraRequest = true;
|
||||
m_PrevFraction = -1.0f;
|
||||
m_ForceSoftwareFallback = false;
|
||||
m_RunUpscalerFilterOnFullResolution = false;
|
||||
|
||||
m_PrevHWScaleWidth = 1.0f;
|
||||
m_PrevHWScaleHeight = 1.0f;
|
||||
m_LastScaledSize = new Vector2Int(0, 0);
|
||||
filter = DynamicResUpscaleFilter.CatmullRom;
|
||||
}
|
||||
|
||||
private struct ScalerContainer
|
||||
{
|
||||
public DynamicResScalePolicyType type;
|
||||
public PerformDynamicRes method;
|
||||
}
|
||||
|
||||
private static DynamicResScalerSlot s_ActiveScalerSlot = DynamicResScalerSlot.User;
|
||||
private static ScalerContainer[] s_ScalerContainers = new ScalerContainer[(int)DynamicResScalerSlot.Count]
|
||||
{
|
||||
new ScalerContainer() { type = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor, method = DefaultDynamicResMethod },
|
||||
new ScalerContainer() { type = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor, method = DefaultDynamicResMethod }
|
||||
};
|
||||
|
||||
// Debug
|
||||
private Vector2Int cachedOriginalSize;
|
||||
|
||||
/// <summary>
|
||||
/// The filter that is used to upscale the rendering result to the native resolution.
|
||||
/// </summary>
|
||||
public DynamicResUpscaleFilter filter { get; private set; }
|
||||
|
||||
// Used to detect the filters set via user API
|
||||
static Dictionary<int, DynamicResUpscaleFilter> s_CameraUpscaleFilters = new Dictionary<int, DynamicResUpscaleFilter>();
|
||||
|
||||
/// <summary>
|
||||
/// The viewport of the final buffer. This is likely the resolution the dynamic resolution starts from before any scaling. Note this is NOT the target resolution the rendering will happen in
|
||||
/// but the resolution the scaled rendered result will be upscaled to.
|
||||
/// </summary>
|
||||
public Vector2Int finalViewport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// By default, dynamic resolution scaling is turned off automatically when the source matches the final viewport (100% scale).
|
||||
/// That is, DynamicResolutionEnabled and SoftwareDynamicResIsEnabled will return false if the scale is 100%.
|
||||
/// For certain upscalers, we dont want this behavior since they could possibly include anti aliasing and other quality improving post processes.
|
||||
/// Setting this to true will eliminate this behavior.
|
||||
/// Note: when the EdgeAdaptiveScalingUpres (FSR 1.0) filter is set, this will cause this parameter to always be true.
|
||||
/// </summary>
|
||||
public bool runUpscalerFilterOnFullResolution
|
||||
{
|
||||
set { m_RunUpscalerFilterOnFullResolution = value; }
|
||||
get { return m_RunUpscalerFilterOnFullResolution || filter == DynamicResUpscaleFilter.EdgeAdaptiveScalingUpres; }
|
||||
}
|
||||
|
||||
private DynamicResolutionType type;
|
||||
|
||||
private GlobalDynamicResolutionSettings m_CachedSettings = GlobalDynamicResolutionSettings.NewDefault();
|
||||
|
||||
private const int CameraDictionaryMaxcCapacity = 32;
|
||||
private WeakReference m_OwnerCameraWeakRef = null;
|
||||
private static Dictionary<int, DynamicResolutionHandler> s_CameraInstances = new Dictionary<int, DynamicResolutionHandler>(CameraDictionaryMaxcCapacity);
|
||||
private static DynamicResolutionHandler s_DefaultInstance = new DynamicResolutionHandler();
|
||||
|
||||
private static int s_ActiveCameraId = 0;
|
||||
private static DynamicResolutionHandler s_ActiveInstance = s_DefaultInstance;
|
||||
|
||||
//private global state of ScalableBufferManager
|
||||
private static bool s_ActiveInstanceDirty = true;
|
||||
private static float s_GlobalHwFraction = 1.0f;
|
||||
private static bool s_GlobalHwUpresActive = false;
|
||||
|
||||
private bool FlushScalableBufferManagerState()
|
||||
{
|
||||
if (s_GlobalHwUpresActive == HardwareDynamicResIsEnabled() && s_GlobalHwFraction == m_CurrentFraction)
|
||||
return false;
|
||||
|
||||
s_GlobalHwUpresActive = HardwareDynamicResIsEnabled();
|
||||
s_GlobalHwFraction = m_CurrentFraction;
|
||||
float currentFraction = s_GlobalHwUpresActive ? s_GlobalHwFraction : 1.0f;
|
||||
ScalableBufferManager.ResizeBuffers(currentFraction, currentFraction);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static DynamicResolutionHandler GetOrCreateDrsInstanceHandler(Camera camera)
|
||||
{
|
||||
if (camera == null)
|
||||
return null;
|
||||
|
||||
DynamicResolutionHandler instance = null;
|
||||
var key = camera.GetInstanceID();
|
||||
if (!s_CameraInstances.TryGetValue(key, out instance))
|
||||
{
|
||||
//if this camera is not available in the map of cameras lets try creating one.
|
||||
|
||||
//first and foremost, if we exceed the dictionary capacity, lets try and recycle an object that is dead.
|
||||
if (s_CameraInstances.Count >= CameraDictionaryMaxcCapacity)
|
||||
{
|
||||
int recycledInstanceKey = 0;
|
||||
DynamicResolutionHandler recycledInstance = null;
|
||||
foreach (var kv in s_CameraInstances)
|
||||
{
|
||||
//is this object dead? that is, belongs to a camera that was destroyed?
|
||||
if (kv.Value.m_OwnerCameraWeakRef == null || !kv.Value.m_OwnerCameraWeakRef.IsAlive)
|
||||
{
|
||||
recycledInstance = kv.Value;
|
||||
recycledInstanceKey = kv.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (recycledInstance != null)
|
||||
{
|
||||
instance = recycledInstance;
|
||||
s_CameraInstances.Remove(recycledInstanceKey);
|
||||
s_CameraUpscaleFilters.Remove(recycledInstanceKey);
|
||||
}
|
||||
}
|
||||
|
||||
//if we didnt find a dead object, we create one from scratch.
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new DynamicResolutionHandler();
|
||||
instance.m_OwnerCameraWeakRef = new WeakReference(camera);
|
||||
}
|
||||
else
|
||||
{
|
||||
//otherwise, we found a dead object, lets reset it, and have a weak ref to this camera,
|
||||
//so we can possibly recycle it in the future by checking the camera's weak pointer state.
|
||||
instance.Reset();
|
||||
instance.m_OwnerCameraWeakRef.Target = camera;
|
||||
}
|
||||
|
||||
s_CameraInstances.Add(key, instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The scheduling mechanism to apply upscaling.
|
||||
/// </summary>
|
||||
public enum UpsamplerScheduleType
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that upscaling must happen before post processing.
|
||||
/// This means that everything runs at the source resolution during rasterization, and post processes will
|
||||
/// run at full resolution. Ideal for temporal upscalers.
|
||||
/// </summary>
|
||||
BeforePost,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that upscaling must happen after post processing.
|
||||
/// This means that everything in the frame runs at the source resolution, and upscaling happens after
|
||||
/// the final pass. This is ideal for spatial upscalers.
|
||||
/// </summary>
|
||||
AfterPost
|
||||
}
|
||||
|
||||
private UpsamplerScheduleType m_UpsamplerSchedule = UpsamplerScheduleType.AfterPost;
|
||||
|
||||
/// <summary>
|
||||
/// Property that sets / gets the state of the upscaling schedule.
|
||||
/// This must be set at the beginning of the frame, once per camera.
|
||||
/// </summary>
|
||||
public UpsamplerScheduleType upsamplerSchedule { set { m_UpsamplerSchedule = value; } get { return m_UpsamplerSchedule; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the instance of the global dynamic resolution handler.
|
||||
/// </summary>
|
||||
public static DynamicResolutionHandler instance { get { return s_ActiveInstance; } }
|
||||
|
||||
|
||||
private DynamicResolutionHandler()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
// TODO: Eventually we will need to provide a good default implementation for this.
|
||||
static private float DefaultDynamicResMethod()
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
private void ProcessSettings(GlobalDynamicResolutionSettings settings)
|
||||
{
|
||||
m_Enabled = settings.enabled && (Application.isPlaying || settings.forceResolution);
|
||||
|
||||
if (!m_Enabled)
|
||||
{
|
||||
m_CurrentFraction = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = settings.dynResType;
|
||||
m_UseMipBias = settings.useMipBias;
|
||||
float minScreenFrac = Mathf.Clamp(settings.minPercentage / 100.0f, 0.1f, 1.0f);
|
||||
m_MinScreenFraction = minScreenFrac;
|
||||
float maxScreenFrac = Mathf.Clamp(settings.maxPercentage / 100.0f, m_MinScreenFraction, 3.0f);
|
||||
m_MaxScreenFraction = maxScreenFrac;
|
||||
|
||||
// Check if a filter has been set via user API, if so we use that, otherwise we use the default from the GlobalDynamicResolutionSettings
|
||||
bool hasUserRequestedFilter = s_CameraUpscaleFilters.TryGetValue(s_ActiveCameraId, out DynamicResUpscaleFilter requestedFilter);
|
||||
|
||||
filter = hasUserRequestedFilter ? requestedFilter : settings.upsampleFilter;
|
||||
m_ForcingRes = settings.forceResolution;
|
||||
|
||||
if (m_ForcingRes)
|
||||
{
|
||||
float fraction = Mathf.Clamp(settings.forcedPercentage / 100.0f, 0.1f, 1.5f);
|
||||
m_CurrentFraction = fraction;
|
||||
}
|
||||
}
|
||||
m_CachedSettings = settings;
|
||||
}
|
||||
|
||||
public Vector2 GetResolvedScale()
|
||||
{
|
||||
if (!m_Enabled || !m_CurrentCameraRequest)
|
||||
{
|
||||
return new Vector2(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
float scaleFractionX = m_CurrentFraction;
|
||||
float scaleFractionY = m_CurrentFraction;
|
||||
if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware)
|
||||
{
|
||||
scaleFractionX = ScalableBufferManager.widthScaleFactor;
|
||||
scaleFractionY = ScalableBufferManager.heightScaleFactor;
|
||||
}
|
||||
|
||||
return new Vector2(scaleFractionX, scaleFractionY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the mip bias to apply in the rendering pipeline. This mip bias helps bring detail since sampling of textures occurs at the target rate.
|
||||
/// </summary>
|
||||
/// <param name="inputResolution">The input width x height resolution in pixels.</param>
|
||||
/// <param name="outputResolution">The output width x height resolution in pixels.</param>
|
||||
/// <param name="forceApply">False by default. If true, we ignore the useMipBias setting and return a mip bias regardless.</param>
|
||||
public float CalculateMipBias(Vector2Int inputResolution, Vector2Int outputResolution, bool forceApply = false)
|
||||
{
|
||||
if (!m_UseMipBias && !forceApply)
|
||||
return 0.0f;
|
||||
|
||||
return (float)Math.Log((double)inputResolution.x / (double)outputResolution.x, 2.0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the scaler method used to drive dynamic resolution by the user.
|
||||
/// </summary>
|
||||
/// <param name="scaler">The delegate used to determine the resolution percentage used by the dynamic resolution system.</param>
|
||||
/// <param name="scalerType">The type of scaler that is used, this is used to indicate the return type of the scaler to the dynamic resolution system.</param>
|
||||
static public void SetDynamicResScaler(PerformDynamicRes scaler, DynamicResScalePolicyType scalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
|
||||
{
|
||||
s_ScalerContainers[(int)DynamicResScalerSlot.User] = new ScalerContainer() { type = scalerType, method = scaler };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the scaler method used to drive dynamic resolution internally from the Scriptable Rendering Pipeline. This function should only be called by Scriptable Rendering Pipeline.
|
||||
/// </summary>
|
||||
/// <param name="scaler">The delegate used to determine the resolution percentage used by the dynamic resolution system.</param>
|
||||
/// <param name="scalerType">The type of scaler that is used, this is used to indicate the return type of the scaler to the dynamic resolution system.</param>
|
||||
static public void SetSystemDynamicResScaler(PerformDynamicRes scaler, DynamicResScalePolicyType scalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
|
||||
{
|
||||
s_ScalerContainers[(int)DynamicResScalerSlot.System] = new ScalerContainer() { type = scalerType, method = scaler };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the active dynamic scaler slot to be used by the runtime when calculating frame resolutions.
|
||||
/// See DynamicResScalerSlot for more information.
|
||||
/// </summary>
|
||||
/// <param name="slot">The scaler to be selected and used by the runtime.</param>
|
||||
static public void SetActiveDynamicScalerSlot(DynamicResScalerSlot slot)
|
||||
{
|
||||
s_ActiveScalerSlot = slot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will clear the currently used camera. Use this function to restore the default instance when UpdateAndUseCamera is called.
|
||||
/// </summary>
|
||||
public static void ClearSelectedCamera()
|
||||
{
|
||||
s_ActiveInstance = s_DefaultInstance;
|
||||
s_ActiveCameraId = 0;
|
||||
s_ActiveInstanceDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the Upscale filter used by the camera when dynamic resolution is run.
|
||||
/// </summary>
|
||||
/// <param name="camera">The camera for which the upscale filter is set.</param>
|
||||
/// <param name="filter">The filter to be used by the camera to upscale to final resolution.</param>
|
||||
static public void SetUpscaleFilter(Camera camera, DynamicResUpscaleFilter filter)
|
||||
{
|
||||
var cameraID = camera.GetInstanceID();
|
||||
if (s_CameraUpscaleFilters.ContainsKey(cameraID))
|
||||
{
|
||||
s_CameraUpscaleFilters[cameraID] = filter;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_CameraUpscaleFilters.Add(cameraID, filter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set whether the camera that is currently processed by the pipeline has requested dynamic resolution or not.
|
||||
/// </summary>
|
||||
/// <param name="cameraRequest">Determines whether the camera has requested dynamic resolution or not.</param>
|
||||
public void SetCurrentCameraRequest(bool cameraRequest)
|
||||
{
|
||||
m_CurrentCameraRequest = cameraRequest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the state of the dynamic resolution system for a specific camera.
|
||||
/// Call this function also to switch context between cameras (will set the current camera as active).
|
||||
// Passing a null camera has the same effect as calling Update without the camera parameter.
|
||||
/// </summary>
|
||||
/// <param name="camera">Camera used to select a specific instance tied to this DynamicResolutionHandler instance.
|
||||
/// </param>
|
||||
/// <param name="settings">(optional) The settings that are to be used by the dynamic resolution system. passing null for the settings will result in the last update's settings used.</param>
|
||||
/// <param name="OnResolutionChange">An action that will be called every time the dynamic resolution system triggers a change in resolution.</param>
|
||||
public static void UpdateAndUseCamera(Camera camera, GlobalDynamicResolutionSettings? settings = null, Action OnResolutionChange = null)
|
||||
{
|
||||
int newCameraId;
|
||||
if (camera == null)
|
||||
{
|
||||
s_ActiveInstance = s_DefaultInstance;
|
||||
newCameraId = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_ActiveInstance = GetOrCreateDrsInstanceHandler(camera);
|
||||
newCameraId = camera.GetInstanceID();
|
||||
}
|
||||
|
||||
s_ActiveInstanceDirty = newCameraId != s_ActiveCameraId;
|
||||
s_ActiveCameraId = newCameraId;
|
||||
s_ActiveInstance.Update(settings.HasValue ? settings.Value : s_ActiveInstance.m_CachedSettings, OnResolutionChange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the state of the dynamic resolution system.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings that are to be used by the dynamic resolution system.</param>
|
||||
/// <param name="OnResolutionChange">An action that will be called every time the dynamic resolution system triggers a change in resolution.</param>
|
||||
public void Update(GlobalDynamicResolutionSettings settings, Action OnResolutionChange = null)
|
||||
{
|
||||
ProcessSettings(settings);
|
||||
|
||||
if (!m_Enabled || !s_ActiveInstanceDirty)
|
||||
{
|
||||
FlushScalableBufferManagerState();
|
||||
s_ActiveInstanceDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_ForcingRes)
|
||||
{
|
||||
ref ScalerContainer scaler = ref s_ScalerContainers[(int)s_ActiveScalerSlot];
|
||||
if (scaler.type == DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
|
||||
{
|
||||
float currLerp = scaler.method();
|
||||
float lerpFactor = Mathf.Clamp(currLerp, 0.0f, 1.0f);
|
||||
m_CurrentFraction = Mathf.Lerp(m_MinScreenFraction, m_MaxScreenFraction, lerpFactor);
|
||||
}
|
||||
else if (scaler.type == DynamicResScalePolicyType.ReturnsPercentage)
|
||||
{
|
||||
float percentageRequested = Mathf.Max(scaler.method(), 5.0f);
|
||||
m_CurrentFraction = Mathf.Clamp(percentageRequested / 100.0f, m_MinScreenFraction, m_MaxScreenFraction);
|
||||
}
|
||||
}
|
||||
|
||||
bool hardwareResolutionChanged = false;
|
||||
bool softwareResolutionChanged = m_CurrentFraction != m_PrevFraction;
|
||||
|
||||
m_PrevFraction = m_CurrentFraction;
|
||||
|
||||
if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware)
|
||||
{
|
||||
hardwareResolutionChanged = FlushScalableBufferManagerState();
|
||||
if (ScalableBufferManager.widthScaleFactor != m_PrevHWScaleWidth ||
|
||||
ScalableBufferManager.heightScaleFactor != m_PrevHWScaleHeight)
|
||||
{
|
||||
hardwareResolutionChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((softwareResolutionChanged || hardwareResolutionChanged) && OnResolutionChange != null)
|
||||
OnResolutionChange();
|
||||
|
||||
s_ActiveInstanceDirty = false;
|
||||
m_PrevHWScaleWidth = ScalableBufferManager.widthScaleFactor;
|
||||
m_PrevHWScaleHeight = ScalableBufferManager.heightScaleFactor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether software dynamic resolution is enabled or not.
|
||||
/// </summary>
|
||||
/// <returns>True: Software dynamic resolution is enabled</returns>
|
||||
public bool SoftwareDynamicResIsEnabled()
|
||||
{
|
||||
return m_CurrentCameraRequest && m_Enabled && (m_CurrentFraction != 1.0f || runUpscalerFilterOnFullResolution) && (m_ForceSoftwareFallback || type == DynamicResolutionType.Software);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether hardware dynamic resolution is enabled or not.
|
||||
/// </summary>
|
||||
/// <returns>True: Hardware dynamic resolution is enabled</returns>
|
||||
public bool HardwareDynamicResIsEnabled()
|
||||
{
|
||||
return !m_ForceSoftwareFallback && m_CurrentCameraRequest && m_Enabled && type == DynamicResolutionType.Hardware;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies whether hardware dynamic resolution has been requested and is going to be used.
|
||||
/// </summary>
|
||||
/// <returns>True: Hardware dynamic resolution is requested by user and software fallback has not been forced</returns>
|
||||
public bool RequestsHardwareDynamicResolution()
|
||||
{
|
||||
if (m_ForceSoftwareFallback)
|
||||
return false;
|
||||
|
||||
return type == DynamicResolutionType.Hardware;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies whether dynamic resolution is enabled and scaling the render targets.
|
||||
/// </summary>
|
||||
/// <returns>True: Dynamic resolution is enabled.</returns>
|
||||
public bool DynamicResolutionEnabled()
|
||||
{
|
||||
//we assume that the DRS schedule takes care of anti aliasing. Thus we dont care if the fraction requested is 1.0
|
||||
return m_CurrentCameraRequest && m_Enabled && (m_CurrentFraction != 1.0f || runUpscalerFilterOnFullResolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces software fallback for dynamic resolution. Needs to be called in case Hardware dynamic resolution is requested by the user, but not supported by the platform.
|
||||
/// </summary>
|
||||
public void ForceSoftwareFallback()
|
||||
{
|
||||
m_ForceSoftwareFallback = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies to the passed size the scale imposed by the dynamic resolution system.
|
||||
/// Note: this function has the side effect of caching the last scale size, and the output is always smaller or equal then the input.
|
||||
/// </summary>
|
||||
/// <param name="size">The starting size of the render target that will be scaled by dynamic resolution.</param>
|
||||
/// <returns>The parameter size scaled by the dynamic resolution system.</returns>
|
||||
public Vector2Int GetScaledSize(Vector2Int size)
|
||||
{
|
||||
cachedOriginalSize = size;
|
||||
|
||||
if (!m_Enabled || !m_CurrentCameraRequest)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
Vector2Int scaledSize = ApplyScalesOnSize(size);
|
||||
|
||||
m_LastScaledSize = scaledSize;
|
||||
return scaledSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies to the passed size the scale imposed by the dynamic resolution system.
|
||||
/// This function uses the internal resolved scale from the dynamic resolution system.
|
||||
/// Note: this function is pure (has no side effects), this function does not cache the pre-scale size
|
||||
/// </summary>
|
||||
/// <param name="size">The size to apply the scaling</param>
|
||||
/// <returns>The parameter size scaled by the dynamic resolution system.</returns>
|
||||
public Vector2Int ApplyScalesOnSize(Vector2Int size)
|
||||
{
|
||||
return ApplyScalesOnSize(size, GetResolvedScale());
|
||||
}
|
||||
|
||||
internal Vector2Int ApplyScalesOnSize(Vector2Int size, Vector2 scales)
|
||||
{
|
||||
Vector2Int scaledSize = new Vector2Int(Mathf.CeilToInt(size.x * scales.x), Mathf.CeilToInt(size.y * scales.y));
|
||||
if (m_ForceSoftwareFallback || type != DynamicResolutionType.Hardware)
|
||||
{
|
||||
scaledSize.x += (1 & scaledSize.x);
|
||||
scaledSize.y += (1 & scaledSize.y);
|
||||
}
|
||||
|
||||
scaledSize.x = Math.Min(scaledSize.x, size.x);
|
||||
scaledSize.y = Math.Min(scaledSize.y, size.y);
|
||||
|
||||
return scaledSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the scale that is currently applied by the dynamic resolution system.
|
||||
/// </summary>
|
||||
/// <returns>The scale that is currently applied by the dynamic resolution system.</returns>
|
||||
public float GetCurrentScale()
|
||||
{
|
||||
return (m_Enabled && m_CurrentCameraRequest) ? m_CurrentFraction : 1.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the latest scaled size that has been produced by GetScaledSize.
|
||||
/// </summary>
|
||||
/// <returns>The latest scaled size that has been produced by GetScaledSize.</returns>
|
||||
public Vector2Int GetLastScaledSize()
|
||||
{
|
||||
return m_LastScaledSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the resolved low res multiplier based on the low res transparency threshold settings.
|
||||
/// Note: The pipeline can use this to drive the scale for low res transparency if available.
|
||||
/// </summary>
|
||||
/// <param name="targetLowRes"> the target low resolution.
|
||||
/// If by any chance thresholding is disabled or clamped, the exact same resolution is returned.
|
||||
/// This allows the caller to directly compare the float result safely with the floating point target resolution.
|
||||
/// </param>
|
||||
/// <returns>Returns the resolved low res multiplier based on the low transparency threshold settings.</returns>
|
||||
public float GetLowResMultiplier(float targetLowRes)
|
||||
{
|
||||
if (!m_Enabled)
|
||||
return targetLowRes;
|
||||
|
||||
float thresholdPercentage = Math.Min(m_CachedSettings.lowResTransparencyMinimumThreshold / 100.0f, targetLowRes);
|
||||
float targetPercentage = targetLowRes * m_CurrentFraction;
|
||||
if (targetPercentage >= thresholdPercentage)
|
||||
return targetLowRes;
|
||||
|
||||
return Mathf.Clamp(thresholdPercentage / m_CurrentFraction, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 43b290740b0e7c34ea95e0fd8b38eb68
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Types of dynamic resolution that can be requested. Note that if Hardware is selected, but not available on the platform, the system will fallback to Software.
|
||||
/// </summary>
|
||||
public enum DynamicResolutionType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Software dynamic resolution.
|
||||
/// </summary>
|
||||
Software,
|
||||
/// <summary>
|
||||
/// Hardware dynamic resolution.
|
||||
/// </summary>
|
||||
Hardware,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Types of filters that can be used to upscale rendered result to native resolution.
|
||||
/// </summary>
|
||||
public enum DynamicResUpscaleFilter : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Bilinear upscaling filter. Obsolete and not supported.
|
||||
/// </summary>
|
||||
[Obsolete("Bilinear upscale filter is considered obsolete and is not supported anymore, please use CatmullRom for a very cheap, but blurry filter.", false)] Bilinear,
|
||||
/// <summary>
|
||||
/// Bicubic Catmull-Rom upscaling filter.
|
||||
/// </summary>
|
||||
CatmullRom,
|
||||
/// <summary>
|
||||
/// Lanczos upscaling filter. Obsolete and not supported.
|
||||
/// </summary>
|
||||
[Obsolete("Lanczos upscale filter is considered obsolete and is not supported anymore, please use Contrast Adaptive Sharpening for very sharp filter or FidelityFX Super Resolution 1.0.", false)] Lanczos,
|
||||
/// <summary>
|
||||
/// Contrast Adaptive Sharpening upscaling filter.
|
||||
/// </summary>
|
||||
ContrastAdaptiveSharpen,
|
||||
/// <summary>
|
||||
/// FidelityFX Super Resolution 1.0
|
||||
/// </summary>
|
||||
[InspectorName("FidelityFX Super Resolution 1.0")]
|
||||
EdgeAdaptiveScalingUpres,
|
||||
/// <summary>
|
||||
/// Temporal Upscaling.
|
||||
/// </summary>
|
||||
[InspectorName("TAA Upscale")]
|
||||
TAAU
|
||||
}
|
||||
|
||||
/// <summary>User-facing settings for dynamic resolution.</summary>
|
||||
[Serializable]
|
||||
public struct GlobalDynamicResolutionSettings
|
||||
{
|
||||
/// <summary>Default GlobalDynamicResolutionSettings</summary>
|
||||
/// <returns></returns>
|
||||
public static GlobalDynamicResolutionSettings NewDefault() => new GlobalDynamicResolutionSettings()
|
||||
{
|
||||
useMipBias = false,
|
||||
maxPercentage = 100.0f,
|
||||
minPercentage = 100.0f,
|
||||
// It fall-backs to software when not supported, so it makes sense to have it on by default.
|
||||
dynResType = DynamicResolutionType.Hardware,
|
||||
upsampleFilter = DynamicResUpscaleFilter.CatmullRom,
|
||||
forcedPercentage = 100.0f,
|
||||
lowResTransparencyMinimumThreshold = 0.0f,
|
||||
rayTracingHalfResThreshold = 50.0f,
|
||||
|
||||
//Defaults for dlss
|
||||
enableDLSS = false,
|
||||
DLSSUseOptimalSettings = true,
|
||||
DLSSPerfQualitySetting = 0,
|
||||
DLSSSharpness = 0.5f
|
||||
};
|
||||
|
||||
/// <summary>Select whether the dynamic resolution is enabled or not.</summary>
|
||||
public bool enabled;
|
||||
/// <summary>Offsets the mip bias to recover mode detail. This only works if the camera is utilizing TAA.</summary>
|
||||
public bool useMipBias;
|
||||
|
||||
/// <summary>Toggle NVIDIA Deep Learning Super Sampling (DLSS).</summary>
|
||||
public bool enableDLSS;
|
||||
|
||||
/// <summary>Opaque quality setting of NVIDIA Deep Learning Super Sampling (DLSS). Use the system enum UnityEngine.NVIDIA.DLSSQuality to set the quality.</summary>
|
||||
public uint DLSSPerfQualitySetting;
|
||||
|
||||
/// <summary>Toggle NVIDIA Deep Learning Super Sampling (DLSS) automatic recommendation system for scaling and sharpness.
|
||||
/// If this is on, the manually established scale callback for Dynamic Resolution Scaling is ignored. The sharpness setting of DLSS is also ignored.
|
||||
/// </summary>
|
||||
public bool DLSSUseOptimalSettings;
|
||||
|
||||
/// <summary>Pixel sharpness of NVIDIA Deep Leraning Super Sampling (DLSS).</summary>
|
||||
[Range(0, 1)]
|
||||
public float DLSSSharpness;
|
||||
|
||||
/// <summary>The maximum resolution percentage that dynamic resolution can reach.</summary>
|
||||
public float maxPercentage;
|
||||
/// <summary>The minimum resolution percentage that dynamic resolution can reach.</summary>
|
||||
public float minPercentage;
|
||||
|
||||
/// <summary>The type of dynamic resolution method.</summary>
|
||||
public DynamicResolutionType dynResType;
|
||||
/// <summary>The default of upscaling filter used. It can be overridden via the API DynamicResolutionHandler.SetUpscaleFilter </summary>
|
||||
public DynamicResUpscaleFilter upsampleFilter;
|
||||
|
||||
/// <summary>Select whether dynamic resolution system will force a specific resolution percentage.</summary>
|
||||
public bool forceResolution;
|
||||
/// <summary>The resolution percentage forced in case forceResolution is set to true.</summary>
|
||||
public float forcedPercentage;
|
||||
|
||||
/// <summary>The minimum percentage threshold allowed to clamp low resolution transparency. When the resolution percentage falls below this threshold, HDRP will clamp the low resolution to this percentage.</summary>
|
||||
public float lowResTransparencyMinimumThreshold;
|
||||
|
||||
/// <summary>The minimum percentage threshold allowed to render ray tracing effects at half resolution. When the resolution percentage falls below this threshold, HDRP will render ray tracing effects at full resolution.</summary>
|
||||
public float rayTracingHalfResThreshold;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 602e65923ac15cd498c65c02be421511
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to identify additional data components
|
||||
/// </summary>
|
||||
public interface IAdditionalData
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 387193679abe1894fa3e4edf840e8932
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// By implementing this interface, a render pipeline can indicate to external code it supports virtual texturing.
|
||||
/// </summary>
|
||||
public interface IVirtualTexturingEnabledRenderPipeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if virtual texturing is currently enabled for this render pipeline instance.
|
||||
/// </summary>
|
||||
bool virtualTexturingEnabled { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: abbe08f5688bd7342b997cf075044945
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// A list that stores value on a provided memory buffer.
|
||||
///
|
||||
/// Usually use this to have a list on stack allocated memory.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data stored in the list.</typeparam>
|
||||
public unsafe struct ListBuffer<T>
|
||||
where T : unmanaged
|
||||
{
|
||||
private T* m_BufferPtr;
|
||||
private int m_Capacity;
|
||||
private int* m_CountPtr;
|
||||
|
||||
/// <summary>
|
||||
/// The pointer to the memory storage.
|
||||
/// </summary>
|
||||
internal T* BufferPtr => m_BufferPtr;
|
||||
|
||||
/// <summary>
|
||||
/// The number of item in the list.
|
||||
/// </summary>
|
||||
public int Count => *m_CountPtr;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of item stored in this list.
|
||||
/// </summary>
|
||||
public int Capacity => m_Capacity;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiate a new list.
|
||||
/// </summary>
|
||||
/// <param name="bufferPtr">The address in memory to store the data.</param>
|
||||
/// <param name="countPtr">The address in memory to store the number of item of this list..</param>
|
||||
/// <param name="capacity">The number of <typeparamref name="T"/> that can be stored in the buffer.</param>
|
||||
public ListBuffer(T* bufferPtr, int* countPtr, int capacity)
|
||||
{
|
||||
m_BufferPtr = bufferPtr;
|
||||
m_Capacity = capacity;
|
||||
m_CountPtr = countPtr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an item from the list.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to get.</param>
|
||||
/// <returns>A reference to the item.</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">If the index is invalid.</exception>
|
||||
public ref T this[in int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
throw new IndexOutOfRangeException(
|
||||
$"Expected a value between 0 and {Count}, but received {index}.");
|
||||
return ref m_BufferPtr[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an item from the list.
|
||||
///
|
||||
/// Safety: index must be inside the bounds of the list.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to get.</param>
|
||||
/// <returns>A reference to the item.</returns>
|
||||
public unsafe ref T GetUnchecked(in int index) => ref m_BufferPtr[index];
|
||||
|
||||
/// <summary>
|
||||
/// Try to add a value in the list.
|
||||
/// </summary>
|
||||
/// <param name="value">A reference to the value to add.</param>
|
||||
/// <returns>
|
||||
/// <code>true</code> when the value was added,
|
||||
/// <code>false</code> when the value was not added because the capacity was reached.
|
||||
/// </returns>
|
||||
public bool TryAdd(in T value)
|
||||
{
|
||||
if (Count >= m_Capacity)
|
||||
return false;
|
||||
|
||||
m_BufferPtr[Count] = value;
|
||||
++*m_CountPtr;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the content of this list into another buffer in memory.
|
||||
///
|
||||
/// Safety:
|
||||
/// * The destination must have enough memory to receive the copied data.
|
||||
/// </summary>
|
||||
/// <param name="dstBuffer">The destination buffer of the copy operation.</param>
|
||||
/// <param name="startDstIndex">The index of the first element that will be copied in the destination buffer.</param>
|
||||
/// <param name="copyCount">The number of item to copy.</param>
|
||||
public unsafe void CopyTo(T* dstBuffer, int startDstIndex, int copyCount)
|
||||
{
|
||||
UnsafeUtility.MemCpy(dstBuffer + startDstIndex, m_BufferPtr,
|
||||
UnsafeUtility.SizeOf<T>() * copyCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to copy the list into another list.
|
||||
/// </summary>
|
||||
/// <param name="other">The destination of the copy.</param>
|
||||
/// <returns>
|
||||
/// * <code>true</code> when the copy was performed.
|
||||
/// * <code>false</code> when the copy was aborted because the destination have a capacity too small.
|
||||
/// </returns>
|
||||
public bool TryCopyTo(ListBuffer<T> other)
|
||||
{
|
||||
if (other.Count + Count >= other.m_Capacity)
|
||||
return false;
|
||||
|
||||
UnsafeUtility.MemCpy(other.m_BufferPtr + other.Count, m_BufferPtr, UnsafeUtility.SizeOf<T>() * Count);
|
||||
*other.m_CountPtr += Count;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to copy the data from a buffer in this list.
|
||||
/// </summary>
|
||||
/// <param name="srcPtr">The pointer of the source memory to copy.</param>
|
||||
/// <param name="count">The number of item to copy from the source buffer.</param>
|
||||
/// <returns>
|
||||
/// * <code>true</code> when the copy was performed.
|
||||
/// * <code>false</code> when the copy was aborted because the capacity of this list is too small.
|
||||
/// </returns>
|
||||
public bool TryCopyFrom(T* srcPtr, int count)
|
||||
{
|
||||
if (count + Count > m_Capacity)
|
||||
return false;
|
||||
|
||||
UnsafeUtility.MemCpy(m_BufferPtr + Count, srcPtr, UnsafeUtility.SizeOf<T>() * count);
|
||||
*m_CountPtr += count;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="ListBuffer{T}"/>.
|
||||
/// </summary>
|
||||
public static class ListBufferExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform a quick sort on a <see cref="ListBuffer{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="self">The list to sort.</param>
|
||||
/// <typeparam name="T">The type of the element in the list.</typeparam>
|
||||
public static void QuickSort<T>(this ListBuffer<T> self)
|
||||
where T : unmanaged, IComparable<T>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CoreUnsafeUtils.QuickSort<int>(self.Count, self.BufferPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 188d5dc897b64646b3757571725337ce
|
||||
timeCreated: 1591792904
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic object pool.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the object pool.</typeparam>
|
||||
public class ObjectPool<T> where T : new()
|
||||
{
|
||||
readonly Stack<T> m_Stack = new Stack<T>();
|
||||
readonly UnityAction<T> m_ActionOnGet;
|
||||
readonly UnityAction<T> m_ActionOnRelease;
|
||||
readonly bool m_CollectionCheck = true;
|
||||
|
||||
/// <summary>
|
||||
/// Number of objects in the pool.
|
||||
/// </summary>
|
||||
public int countAll { get; private set; }
|
||||
/// <summary>
|
||||
/// Number of active objects in the pool.
|
||||
/// </summary>
|
||||
public int countActive { get { return countAll - countInactive; } }
|
||||
/// <summary>
|
||||
/// Number of inactive objects in the pool.
|
||||
/// </summary>
|
||||
public int countInactive { get { return m_Stack.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="actionOnGet">Action on get.</param>
|
||||
/// <param name="actionOnRelease">Action on release.</param>
|
||||
/// <param name="collectionCheck">True if collection integrity should be checked.</param>
|
||||
public ObjectPool(UnityAction<T> actionOnGet, UnityAction<T> actionOnRelease, bool collectionCheck = true)
|
||||
{
|
||||
m_ActionOnGet = actionOnGet;
|
||||
m_ActionOnRelease = actionOnRelease;
|
||||
m_CollectionCheck = collectionCheck;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an object from the pool.
|
||||
/// </summary>
|
||||
/// <returns>A new object from the pool.</returns>
|
||||
public T Get()
|
||||
{
|
||||
T element;
|
||||
if (m_Stack.Count == 0)
|
||||
{
|
||||
element = new T();
|
||||
countAll++;
|
||||
}
|
||||
else
|
||||
{
|
||||
element = m_Stack.Pop();
|
||||
}
|
||||
if (m_ActionOnGet != null)
|
||||
m_ActionOnGet(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pooled object.
|
||||
/// </summary>
|
||||
public struct PooledObject : IDisposable
|
||||
{
|
||||
readonly T m_ToReturn;
|
||||
readonly ObjectPool<T> m_Pool;
|
||||
|
||||
internal PooledObject(T value, ObjectPool<T> pool)
|
||||
{
|
||||
m_ToReturn = value;
|
||||
m_Pool = pool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposable pattern implementation.
|
||||
/// </summary>
|
||||
void IDisposable.Dispose() => m_Pool.Release(m_ToReturn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get et new PooledObject.
|
||||
/// </summary>
|
||||
/// <param name="v">Output new typed object.</param>
|
||||
/// <returns>New PooledObject</returns>
|
||||
public PooledObject Get(out T v) => new PooledObject(v = Get(), this);
|
||||
|
||||
/// <summary>
|
||||
/// Release an object to the pool.
|
||||
/// </summary>
|
||||
/// <param name="element">Object to release.</param>
|
||||
public void Release(T element)
|
||||
{
|
||||
#if UNITY_EDITOR // keep heavy checks in editor
|
||||
if (m_CollectionCheck && m_Stack.Count > 0)
|
||||
{
|
||||
if (m_Stack.Contains(element))
|
||||
Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
|
||||
}
|
||||
#endif
|
||||
if (m_ActionOnRelease != null)
|
||||
m_ActionOnRelease(element);
|
||||
m_Stack.Push(element);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic pool.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the objects in the pull.</typeparam>
|
||||
public static class GenericPool<T>
|
||||
where T : new()
|
||||
{
|
||||
// Object pool to avoid allocations.
|
||||
static readonly ObjectPool<T> s_Pool = new ObjectPool<T>(null, null);
|
||||
|
||||
/// <summary>
|
||||
/// Get a new object.
|
||||
/// </summary>
|
||||
/// <returns>A new object from the pool.</returns>
|
||||
public static T Get() => s_Pool.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Get a new PooledObject
|
||||
/// </summary>
|
||||
/// <param name="value">Output typed object.</param>
|
||||
/// <returns>A new PooledObject.</returns>
|
||||
public static ObjectPool<T>.PooledObject Get(out T value) => s_Pool.Get(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Release an object to the pool.
|
||||
/// </summary>
|
||||
/// <param name="toRelease">Object to release.</param>
|
||||
public static void Release(T toRelease) => s_Pool.Release(toRelease);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic pool without collection checks.
|
||||
/// This class is an alternative for the GenericPool for object that allocate memory when they are being compared.
|
||||
/// It is the case for the CullingResult class from Unity, and because of this in HDRP HDCullingResults generates garbage whenever we use ==, .Equals or ReferenceEquals.
|
||||
/// This pool doesn't do any of these comparison because we don't check if the stack already contains the element before releasing it.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the objects in the pull.</typeparam>
|
||||
public static class UnsafeGenericPool<T>
|
||||
where T : new()
|
||||
{
|
||||
// Object pool to avoid allocations.
|
||||
static readonly ObjectPool<T> s_Pool = new ObjectPool<T>(null, null, false);
|
||||
|
||||
/// <summary>
|
||||
/// Get a new object.
|
||||
/// </summary>
|
||||
/// <returns>A new object from the pool.</returns>
|
||||
public static T Get() => s_Pool.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Get a new PooledObject
|
||||
/// </summary>
|
||||
/// <param name="value">Output typed object.</param>
|
||||
/// <returns>A new PooledObject.</returns>
|
||||
public static ObjectPool<T>.PooledObject Get(out T value) => s_Pool.Get(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Release an object to the pool.
|
||||
/// </summary>
|
||||
/// <param name="toRelease">Object to release.</param>
|
||||
public static void Release(T toRelease) => s_Pool.Release(toRelease);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List Pool.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the objects in the pooled lists.</typeparam>
|
||||
public static class ListPool<T>
|
||||
{
|
||||
// Object pool to avoid allocations.
|
||||
static readonly ObjectPool<List<T>> s_Pool = new ObjectPool<List<T>>(null, l => l.Clear());
|
||||
|
||||
/// <summary>
|
||||
/// Get a new List
|
||||
/// </summary>
|
||||
/// <returns>A new List</returns>
|
||||
public static List<T> Get() => s_Pool.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Get a new list PooledObject.
|
||||
/// </summary>
|
||||
/// <param name="value">Output typed List.</param>
|
||||
/// <returns>A new List PooledObject.</returns>
|
||||
public static ObjectPool<List<T>>.PooledObject Get(out List<T> value) => s_Pool.Get(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Release an object to the pool.
|
||||
/// </summary>
|
||||
/// <param name="toRelease">List to release.</param>
|
||||
public static void Release(List<T> toRelease) => s_Pool.Release(toRelease);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HashSet Pool.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the objects in the pooled hashsets.</typeparam>
|
||||
public static class HashSetPool<T>
|
||||
{
|
||||
// Object pool to avoid allocations.
|
||||
static readonly ObjectPool<HashSet<T>> s_Pool = new ObjectPool<HashSet<T>>(null, l => l.Clear());
|
||||
|
||||
/// <summary>
|
||||
/// Get a new HashSet
|
||||
/// </summary>
|
||||
/// <returns>A new HashSet</returns>
|
||||
public static HashSet<T> Get() => s_Pool.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Get a new list PooledObject.
|
||||
/// </summary>
|
||||
/// <param name="value">Output typed HashSet.</param>
|
||||
/// <returns>A new HashSet PooledObject.</returns>
|
||||
public static ObjectPool<HashSet<T>>.PooledObject Get(out HashSet<T> value) => s_Pool.Get(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Release an object to the pool.
|
||||
/// </summary>
|
||||
/// <param name="toRelease">hashSet to release.</param>
|
||||
public static void Release(HashSet<T> toRelease) => s_Pool.Release(toRelease);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary Pool.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">Key type.</typeparam>
|
||||
/// <typeparam name="TValue">Value type.</typeparam>
|
||||
public static class DictionaryPool<TKey, TValue>
|
||||
{
|
||||
// Object pool to avoid allocations.
|
||||
static readonly ObjectPool<Dictionary<TKey, TValue>> s_Pool
|
||||
= new ObjectPool<Dictionary<TKey, TValue>>(null, l => l.Clear());
|
||||
|
||||
/// <summary>
|
||||
/// Get a new Dictionary
|
||||
/// </summary>
|
||||
/// <returns>A new Dictionary</returns>
|
||||
public static Dictionary<TKey, TValue> Get() => s_Pool.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Get a new dictionary PooledObject.
|
||||
/// </summary>
|
||||
/// <param name="value">Output typed Dictionary.</param>
|
||||
/// <returns>A new Dictionary PooledObject.</returns>
|
||||
public static ObjectPool<Dictionary<TKey, TValue>>.PooledObject Get(out Dictionary<TKey, TValue> value)
|
||||
=> s_Pool.Get(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Release an object to the pool.
|
||||
/// </summary>
|
||||
/// <param name="toRelease">Dictionary to release.</param>
|
||||
public static void Release(Dictionary<TKey, TValue> toRelease) => s_Pool.Release(toRelease);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fa6940bea9f1edf4ea661175c511ca11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// On List Changed Event Args.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">List type.</typeparam>
|
||||
public sealed class ListChangedEventArgs<T> : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Index
|
||||
/// </summary>
|
||||
public readonly int index;
|
||||
/// <summary>
|
||||
/// Item
|
||||
/// </summary>
|
||||
public readonly T item;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="index">Index</param>
|
||||
/// <param name="item">Item</param>
|
||||
public ListChangedEventArgs(int index, T item)
|
||||
{
|
||||
this.index = index;
|
||||
this.item = item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List changed event handler.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">List type.</typeparam>
|
||||
/// <param name="sender">Sender.</param>
|
||||
/// <param name="e">List changed even arguments.</param>
|
||||
public delegate void ListChangedEventHandler<T>(ObservableList<T> sender, ListChangedEventArgs<T> e);
|
||||
|
||||
/// <summary>
|
||||
/// Observable list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the list.</typeparam>
|
||||
public class ObservableList<T> : IList<T>
|
||||
{
|
||||
IList<T> m_List;
|
||||
|
||||
/// <summary>
|
||||
/// Added item event.
|
||||
/// </summary>
|
||||
public event ListChangedEventHandler<T> ItemAdded;
|
||||
/// <summary>
|
||||
/// Removed item event.
|
||||
/// </summary>
|
||||
public event ListChangedEventHandler<T> ItemRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Accessor.
|
||||
/// </summary>
|
||||
/// <param name="index">Item index.</param>
|
||||
/// <returns>The item at the provided index.</returns>
|
||||
public T this[int index]
|
||||
{
|
||||
get { return m_List[index]; }
|
||||
set
|
||||
{
|
||||
OnEvent(ItemRemoved, index, m_List[index]);
|
||||
m_List[index] = value;
|
||||
OnEvent(ItemAdded, index, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of elements in the list.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return m_List.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the list read only?
|
||||
/// </summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default Constructor.
|
||||
/// </summary>
|
||||
public ObservableList()
|
||||
: this(0) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="capacity">Allocation size.</param>
|
||||
public ObservableList(int capacity)
|
||||
{
|
||||
m_List = new List<T>(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="collection">Input list.</param>
|
||||
public ObservableList(IEnumerable<T> collection)
|
||||
{
|
||||
m_List = new List<T>(collection);
|
||||
}
|
||||
|
||||
void OnEvent(ListChangedEventHandler<T> e, int index, T item)
|
||||
{
|
||||
if (e != null)
|
||||
e(this, new ListChangedEventArgs<T>(index, item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if an element is present in the list.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to test against.</param>
|
||||
/// <returns>True if the item is in the list.</returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return m_List.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of an item.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to locate in the list.</param>
|
||||
/// <returns>The index of the item in the list if it exists, -1 otherwise.</returns>
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
return m_List.IndexOf(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an item to the list.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to add to the list.</param>
|
||||
public void Add(T item)
|
||||
{
|
||||
m_List.Add(item);
|
||||
OnEvent(ItemAdded, m_List.IndexOf(item), item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add multiple objects to the list.
|
||||
/// </summary>
|
||||
/// <param name="items">Items to add to the list.</param>
|
||||
public void Add(params T[] items)
|
||||
{
|
||||
foreach (var i in items)
|
||||
Add(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert an item in the list.
|
||||
/// </summary>
|
||||
/// <param name="index">Index at which to insert the new item.</param>
|
||||
/// <param name="item">Item to insert in the list.</param>
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
m_List.Insert(index, item);
|
||||
OnEvent(ItemAdded, index, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove an item from the list.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to remove from the list.</param>
|
||||
/// <returns>True if the item was successfuly removed. False otherise.</returns>
|
||||
public bool Remove(T item)
|
||||
{
|
||||
int index = m_List.IndexOf(item);
|
||||
bool ret = m_List.Remove(item);
|
||||
if (ret)
|
||||
OnEvent(ItemRemoved, index, item);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove multiple items from the list.
|
||||
/// </summary>
|
||||
/// <param name="items">Items to remove from the list.</param>
|
||||
/// <returns>The number of removed items.</returns>
|
||||
public int Remove(params T[] items)
|
||||
{
|
||||
if (items == null)
|
||||
return 0;
|
||||
|
||||
int count = 0;
|
||||
|
||||
foreach (var i in items)
|
||||
count += Remove(i) ? 1 : 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove an item at a specific index.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the item to remove.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
var item = m_List[index];
|
||||
m_List.RemoveAt(index);
|
||||
OnEvent(ItemRemoved, index, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the list.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
RemoveAt(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy items in the list to an array.
|
||||
/// </summary>
|
||||
/// <param name="array">Destination array.</param>
|
||||
/// <param name="arrayIndex">Starting index.</param>
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
m_List.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get enumerator.
|
||||
/// </summary>
|
||||
/// <returns>The list enumerator.</returns>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_List.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get enumerator.
|
||||
/// </summary>
|
||||
/// <returns>The list enumerator.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1511526f1e7c8e94ea477c5b5c9dcc32
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
namespace UnityEngine.Rendering
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Class to serizalize Enum as string and recover it's state
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class SerializableEnum
|
||||
{
|
||||
[SerializeField]
|
||||
private string m_EnumValueAsString;
|
||||
|
||||
[SerializeField]
|
||||
private Type m_EnumType;
|
||||
|
||||
/// <summary>
|
||||
/// Value of enum
|
||||
/// </summary>
|
||||
public Enum value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Enum.TryParse(m_EnumType, m_EnumValueAsString, out object result))
|
||||
return (Enum)result;
|
||||
|
||||
return default(Enum);
|
||||
}
|
||||
set
|
||||
{
|
||||
m_EnumValueAsString = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an enum to be serialized with a type
|
||||
/// </summary>
|
||||
/// <param name="enumType">The underliying type of the enum</param>
|
||||
public SerializableEnum(Type enumType)
|
||||
{
|
||||
m_EnumType = enumType;
|
||||
m_EnumValueAsString = Enum.GetNames(enumType)[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7bbce3ae0d13a4240a1d7531d1e00b3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
//
|
||||
// Unity can't serialize Dictionary so here's a custom wrapper that does. Note that you have to
|
||||
// extend it before it can be serialized as Unity won't serialized generic-based types either.
|
||||
//
|
||||
// Example:
|
||||
// public sealed class MyDictionary : SerializedDictionary<KeyType, ValueType> {}
|
||||
//
|
||||
/// <summary>
|
||||
/// Serialized Dictionary
|
||||
/// </summary>
|
||||
/// <typeparam name="K">Key Type</typeparam>
|
||||
/// <typeparam name="V">Value Type</typeparam>
|
||||
[Serializable]
|
||||
public class SerializedDictionary<K, V> : SerializedDictionary<K, V, K, V>
|
||||
{
|
||||
public override K SerializeKey(K key) => key;
|
||||
public override V SerializeValue(V val) => val;
|
||||
public override K DeserializeKey(K key) => key;
|
||||
public override V DeserializeValue(V val) => val;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public abstract class SerializedDictionary<K, V, SK, SV> : Dictionary<K, V>, ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField]
|
||||
List<SK> m_Keys = new List<SK>();
|
||||
|
||||
[SerializeField]
|
||||
List<SV> m_Values = new List<SV>();
|
||||
|
||||
/// <summary>
|
||||
/// Serialize key K to SK
|
||||
/// </summary>
|
||||
public abstract SK SerializeKey(K key);
|
||||
/// <summary>
|
||||
/// Serialize value V to SV
|
||||
/// </summary>
|
||||
public abstract SV SerializeValue(V value);
|
||||
/// <summary>
|
||||
/// Deserialize key SK to K
|
||||
/// </summary>
|
||||
public abstract K DeserializeKey(SK serializedKey);
|
||||
/// <summary>
|
||||
/// Deserialize value SV to V
|
||||
/// </summary>
|
||||
public abstract V DeserializeValue(SV serializedValue);
|
||||
|
||||
/// <summary>
|
||||
/// OnBeforeSerialize implementation.
|
||||
/// </summary>
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
m_Keys.Clear();
|
||||
m_Values.Clear();
|
||||
|
||||
foreach (var kvp in this)
|
||||
{
|
||||
m_Keys.Add(SerializeKey(kvp.Key));
|
||||
m_Values.Add(SerializeValue(kvp.Value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAfterDeserialize implementation.
|
||||
/// </summary>
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
for (int i = 0; i < m_Keys.Count; i++)
|
||||
Add(DeserializeKey(m_Keys[i]), DeserializeValue(m_Values[i]));
|
||||
|
||||
m_Keys.Clear();
|
||||
m_Values.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b21ca88b322a62d4b9a511ddabc12251
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
using System;
|
||||
using UnityEditor;
|
||||
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
using UnityEngine.XR;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// XRGraphics insulates SRP from API changes across platforms, Editor versions, and as XR transitions into XR SDK
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class XRGraphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Stereo Rendering Modes.
|
||||
/// </summary>
|
||||
public enum StereoRenderingMode
|
||||
{
|
||||
/// <summary>Multi Pass.</summary>
|
||||
MultiPass = 0,
|
||||
/// <summary>Single Pass.</summary>
|
||||
SinglePass,
|
||||
/// <summary>Single Pass Instanced.</summary>
|
||||
SinglePassInstanced,
|
||||
/// <summary>Single Pass Multi View.</summary>
|
||||
SinglePassMultiView
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Eye texture resolution scale.
|
||||
/// </summary>
|
||||
public static float eyeTextureResolutionScale
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.eyeTextureResolutionScale;
|
||||
#endif
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
XRSettings.eyeTextureResolutionScale = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Render viewport scale.
|
||||
/// </summary>
|
||||
public static float renderViewportScale
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.renderViewportScale;
|
||||
#endif
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try enable.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
// TryEnable gets updated before "play" is pressed- we use this for updating GUI only.
|
||||
public static bool tryEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
return false;
|
||||
#else
|
||||
return UnityEditorInternal.VR.VREditor.GetVREnabledOnTargetGroup(BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// SRP should use this to safely determine whether XR is enabled at runtime.
|
||||
/// </summary>
|
||||
public static bool enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
return XRSettings.enabled;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the XR device is active.
|
||||
/// </summary>
|
||||
public static bool isDeviceActive
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.isDeviceActive;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the loaded XR device.
|
||||
/// </summary>
|
||||
public static string loadedDeviceName
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.loadedDeviceName;
|
||||
#endif
|
||||
return "No XR device loaded";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of supported XR devices.
|
||||
/// </summary>
|
||||
public static string[] supportedDevices
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.supportedDevices;
|
||||
#endif
|
||||
return new string[1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stereo rendering mode.
|
||||
/// </summary>
|
||||
public static StereoRenderingMode stereoRenderingMode
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return (StereoRenderingMode)XRSettings.stereoRenderingMode;
|
||||
#endif
|
||||
|
||||
return StereoRenderingMode.SinglePass;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eye texture descriptor.
|
||||
/// </summary>
|
||||
public static RenderTextureDescriptor eyeTextureDesc
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.eyeTextureDesc;
|
||||
#endif
|
||||
return new RenderTextureDescriptor(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eye texture width.
|
||||
/// </summary>
|
||||
public static int eyeTextureWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.eyeTextureWidth;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eye texture height.
|
||||
/// </summary>
|
||||
public static int eyeTextureHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_VR_MODULE
|
||||
if (enabled)
|
||||
return XRSettings.eyeTextureHeight;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5133c1dbdb17436449b17269e791cb3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58a02e8711d94134393b2a8ac22b96ca
|
||||
folderAsset: yes
|
||||
timeCreated: 1491989728
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,490 @@
|
|||
#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
|
||||
#define USE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.EnhancedTouch;
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
internal enum DebugAction
|
||||
{
|
||||
EnableDebugMenu,
|
||||
PreviousDebugPanel,
|
||||
NextDebugPanel,
|
||||
Action,
|
||||
MakePersistent,
|
||||
MoveVertical,
|
||||
MoveHorizontal,
|
||||
Multiplier,
|
||||
ResetAll,
|
||||
DebugActionCount
|
||||
}
|
||||
|
||||
enum DebugActionRepeatMode
|
||||
{
|
||||
Never,
|
||||
Delay
|
||||
}
|
||||
|
||||
public sealed partial class DebugManager
|
||||
{
|
||||
const string kEnableDebugBtn1 = "Enable Debug Button 1";
|
||||
const string kEnableDebugBtn2 = "Enable Debug Button 2";
|
||||
const string kDebugPreviousBtn = "Debug Previous";
|
||||
const string kDebugNextBtn = "Debug Next";
|
||||
const string kValidateBtn = "Debug Validate";
|
||||
const string kPersistentBtn = "Debug Persistent";
|
||||
const string kDPadVertical = "Debug Vertical";
|
||||
const string kDPadHorizontal = "Debug Horizontal";
|
||||
const string kMultiplierBtn = "Debug Multiplier";
|
||||
const string kResetBtn = "Debug Reset";
|
||||
const string kEnableDebug = "Enable Debug";
|
||||
|
||||
DebugActionDesc[] m_DebugActions;
|
||||
DebugActionState[] m_DebugActionStates;
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
InputActionMap debugActionMap = new InputActionMap("Debug Menu");
|
||||
#endif
|
||||
|
||||
void RegisterActions()
|
||||
{
|
||||
m_DebugActions = new DebugActionDesc[(int)DebugAction.DebugActionCount];
|
||||
m_DebugActionStates = new DebugActionState[(int)DebugAction.DebugActionCount];
|
||||
|
||||
var enableDebugMenu = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
enableDebugMenu.buttonAction = debugActionMap.FindAction(kEnableDebug);
|
||||
#else
|
||||
enableDebugMenu.buttonTriggerList.Add(new[] { kEnableDebugBtn1, kEnableDebugBtn2 });
|
||||
enableDebugMenu.keyTriggerList.Add(new[] { KeyCode.LeftControl, KeyCode.Backspace });
|
||||
#endif
|
||||
enableDebugMenu.repeatMode = DebugActionRepeatMode.Never;
|
||||
AddAction(DebugAction.EnableDebugMenu, enableDebugMenu);
|
||||
|
||||
var resetDebugMenu = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
resetDebugMenu.buttonAction = debugActionMap.FindAction(kResetBtn);
|
||||
#else
|
||||
resetDebugMenu.keyTriggerList.Add(new[] { KeyCode.LeftAlt, KeyCode.Backspace });
|
||||
resetDebugMenu.buttonTriggerList.Add(new[] { kResetBtn, kEnableDebugBtn2 });
|
||||
#endif
|
||||
resetDebugMenu.repeatMode = DebugActionRepeatMode.Never;
|
||||
AddAction(DebugAction.ResetAll, resetDebugMenu);
|
||||
|
||||
var nextDebugPanel = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
nextDebugPanel.buttonAction = debugActionMap.FindAction(kDebugNextBtn);
|
||||
#else
|
||||
nextDebugPanel.buttonTriggerList.Add(new[] { kDebugNextBtn });
|
||||
#endif
|
||||
nextDebugPanel.repeatMode = DebugActionRepeatMode.Never;
|
||||
AddAction(DebugAction.NextDebugPanel, nextDebugPanel);
|
||||
|
||||
var previousDebugPanel = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
previousDebugPanel.buttonAction = debugActionMap.FindAction(kDebugPreviousBtn);
|
||||
#else
|
||||
previousDebugPanel.buttonTriggerList.Add(new[] { kDebugPreviousBtn });
|
||||
#endif
|
||||
previousDebugPanel.repeatMode = DebugActionRepeatMode.Never;
|
||||
AddAction(DebugAction.PreviousDebugPanel, previousDebugPanel);
|
||||
|
||||
var validate = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
validate.buttonAction = debugActionMap.FindAction(kValidateBtn);
|
||||
#else
|
||||
validate.buttonTriggerList.Add(new[] { kValidateBtn });
|
||||
#endif
|
||||
validate.repeatMode = DebugActionRepeatMode.Never;
|
||||
AddAction(DebugAction.Action, validate);
|
||||
|
||||
var persistent = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
persistent.buttonAction = debugActionMap.FindAction(kPersistentBtn);
|
||||
#else
|
||||
persistent.buttonTriggerList.Add(new[] { kPersistentBtn });
|
||||
#endif
|
||||
persistent.repeatMode = DebugActionRepeatMode.Never;
|
||||
AddAction(DebugAction.MakePersistent, persistent);
|
||||
|
||||
var multiplier = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
multiplier.buttonAction = debugActionMap.FindAction(kMultiplierBtn);
|
||||
#else
|
||||
multiplier.buttonTriggerList.Add(new[] { kMultiplierBtn });
|
||||
#endif
|
||||
multiplier.repeatMode = DebugActionRepeatMode.Delay;
|
||||
validate.repeatDelay = 0f;
|
||||
|
||||
AddAction(DebugAction.Multiplier, multiplier);
|
||||
|
||||
var moveVertical = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
moveVertical.buttonAction = debugActionMap.FindAction(kDPadVertical);
|
||||
#else
|
||||
moveVertical.axisTrigger = kDPadVertical;
|
||||
#endif
|
||||
moveVertical.repeatMode = DebugActionRepeatMode.Delay;
|
||||
moveVertical.repeatDelay = 0.16f;
|
||||
AddAction(DebugAction.MoveVertical, moveVertical);
|
||||
|
||||
var moveHorizontal = new DebugActionDesc();
|
||||
#if USE_INPUT_SYSTEM
|
||||
moveHorizontal.buttonAction = debugActionMap.FindAction(kDPadHorizontal);
|
||||
#else
|
||||
moveHorizontal.axisTrigger = kDPadHorizontal;
|
||||
#endif
|
||||
moveHorizontal.repeatMode = DebugActionRepeatMode.Delay;
|
||||
moveHorizontal.repeatDelay = 0.16f;
|
||||
AddAction(DebugAction.MoveHorizontal, moveHorizontal);
|
||||
}
|
||||
|
||||
internal void EnableInputActions()
|
||||
{
|
||||
#if USE_INPUT_SYSTEM
|
||||
foreach (var action in debugActionMap)
|
||||
action.Enable();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddAction(DebugAction action, DebugActionDesc desc)
|
||||
{
|
||||
int index = (int)action;
|
||||
m_DebugActions[index] = desc;
|
||||
m_DebugActionStates[index] = new DebugActionState();
|
||||
}
|
||||
|
||||
void SampleAction(int actionIndex)
|
||||
{
|
||||
var desc = m_DebugActions[actionIndex];
|
||||
var state = m_DebugActionStates[actionIndex];
|
||||
|
||||
// Disable all input events if we're using the new input system
|
||||
#if USE_INPUT_SYSTEM
|
||||
if (state.runningAction == false)
|
||||
{
|
||||
if (desc.buttonAction != null)
|
||||
{
|
||||
var value = desc.buttonAction.ReadValue<float>();
|
||||
if (!Mathf.Approximately(value, 0))
|
||||
state.TriggerWithButton(desc.buttonAction, value);
|
||||
}
|
||||
}
|
||||
#elif ENABLE_LEGACY_INPUT_MANAGER
|
||||
//bool canSampleAction = (state.actionTriggered == false) || (desc.repeatMode == DebugActionRepeatMode.Delay && state.timer > desc.repeatDelay);
|
||||
if (state.runningAction == false)
|
||||
{
|
||||
// Check button triggers
|
||||
for (int buttonListIndex = 0; buttonListIndex < desc.buttonTriggerList.Count; ++buttonListIndex)
|
||||
{
|
||||
var buttons = desc.buttonTriggerList[buttonListIndex];
|
||||
bool allButtonPressed = true;
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
allButtonPressed = Input.GetButton(button);
|
||||
if (!allButtonPressed)
|
||||
break;
|
||||
}
|
||||
|
||||
if (allButtonPressed)
|
||||
{
|
||||
state.TriggerWithButton(buttons, 1f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check axis triggers
|
||||
if (desc.axisTrigger != "")
|
||||
{
|
||||
float axisValue = Input.GetAxis(desc.axisTrigger);
|
||||
|
||||
if (axisValue != 0f)
|
||||
state.TriggerWithAxis(desc.axisTrigger, axisValue);
|
||||
}
|
||||
|
||||
// Check key triggers
|
||||
for (int keyListIndex = 0; keyListIndex < desc.keyTriggerList.Count; ++keyListIndex)
|
||||
{
|
||||
bool allKeyPressed = true;
|
||||
|
||||
var keys = desc.keyTriggerList[keyListIndex];
|
||||
foreach (var key in keys)
|
||||
{
|
||||
allKeyPressed = Input.GetKey(key);
|
||||
if (!allKeyPressed)
|
||||
break;
|
||||
}
|
||||
|
||||
if (allKeyPressed)
|
||||
{
|
||||
state.TriggerWithKey(keys, 1f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateAction(int actionIndex)
|
||||
{
|
||||
var desc = m_DebugActions[actionIndex];
|
||||
var state = m_DebugActionStates[actionIndex];
|
||||
|
||||
if (state.runningAction)
|
||||
state.Update(desc);
|
||||
}
|
||||
|
||||
internal void UpdateActions()
|
||||
{
|
||||
for (int actionIndex = 0; actionIndex < m_DebugActions.Length; ++actionIndex)
|
||||
{
|
||||
UpdateAction(actionIndex);
|
||||
SampleAction(actionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
internal float GetAction(DebugAction action)
|
||||
{
|
||||
return m_DebugActionStates[(int)action].actionState;
|
||||
}
|
||||
|
||||
internal bool GetActionToggleDebugMenuWithTouch()
|
||||
{
|
||||
#if USE_INPUT_SYSTEM
|
||||
if (!EnhancedTouchSupport.enabled)
|
||||
return false;
|
||||
|
||||
var touches = InputSystem.EnhancedTouch.Touch.activeTouches;
|
||||
var touchCount = touches.Count;
|
||||
InputSystem.TouchPhase? expectedTouchPhase = null;
|
||||
#else
|
||||
var touches = Input.touches;
|
||||
var touchCount = Input.touchCount;
|
||||
TouchPhase? expectedTouchPhase = TouchPhase.Began;
|
||||
#endif
|
||||
if (touchCount == 3)
|
||||
{
|
||||
foreach (var touch in touches)
|
||||
{
|
||||
// Gesture: 3-finger double-tap
|
||||
if ((!expectedTouchPhase.HasValue || touch.phase == expectedTouchPhase.Value) && touch.tapCount == 2)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool GetActionReleaseScrollTarget()
|
||||
{
|
||||
#if USE_INPUT_SYSTEM
|
||||
bool mouseWheelActive = Mouse.current != null && Mouse.current.scroll.ReadValue() != Vector2.zero;
|
||||
bool touchSupported = Touchscreen.current != null;
|
||||
#else
|
||||
bool mouseWheelActive = Input.mouseScrollDelta != Vector2.zero;
|
||||
bool touchSupported = Input.touchSupported;
|
||||
#endif
|
||||
return mouseWheelActive || touchSupported; // Touchscreens have general problems with scrolling, so it's disabled.
|
||||
}
|
||||
|
||||
void RegisterInputs()
|
||||
{
|
||||
#if UNITY_EDITOR && !USE_INPUT_SYSTEM
|
||||
var inputEntries = new List<InputManagerEntry>
|
||||
{
|
||||
new InputManagerEntry { name = kEnableDebugBtn1, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left ctrl", altBtnPositive = "joystick button 8" },
|
||||
new InputManagerEntry { name = kEnableDebugBtn2, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "backspace", altBtnPositive = "joystick button 9" },
|
||||
new InputManagerEntry { name = kResetBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left alt", altBtnPositive = "joystick button 1" },
|
||||
new InputManagerEntry { name = kDebugNextBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "page down", altBtnPositive = "joystick button 5" },
|
||||
new InputManagerEntry { name = kDebugPreviousBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "page up", altBtnPositive = "joystick button 4" },
|
||||
new InputManagerEntry { name = kValidateBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "return", altBtnPositive = "joystick button 0" },
|
||||
new InputManagerEntry { name = kPersistentBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "right shift", altBtnPositive = "joystick button 2" },
|
||||
new InputManagerEntry { name = kMultiplierBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left shift", altBtnPositive = "joystick button 3" },
|
||||
new InputManagerEntry { name = kDPadHorizontal, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "right", btnNegative = "left", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
|
||||
new InputManagerEntry { name = kDPadVertical, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "up", btnNegative = "down", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
|
||||
new InputManagerEntry { name = kDPadVertical, kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Seventh, btnPositive = "up", btnNegative = "down", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
|
||||
new InputManagerEntry { name = kDPadHorizontal, kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Sixth, btnPositive = "right", btnNegative = "left", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
|
||||
};
|
||||
|
||||
InputRegistering.RegisterInputs(inputEntries);
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
// Register input system actions
|
||||
var enableAction = debugActionMap.AddAction(kEnableDebug, type: InputActionType.Button);
|
||||
enableAction.AddCompositeBinding("ButtonWithOneModifier")
|
||||
.With("Modifier", "<Gamepad>/rightStickPress")
|
||||
.With("Button", "<Gamepad>/leftStickPress")
|
||||
.With("Modifier", "<Keyboard>/leftCtrl")
|
||||
.With("Button", "<Keyboard>/backspace");
|
||||
|
||||
var resetAction = debugActionMap.AddAction(kResetBtn, type: InputActionType.Button);
|
||||
resetAction.AddCompositeBinding("ButtonWithOneModifier")
|
||||
.With("Modifier", "<Gamepad>/rightStickPress")
|
||||
.With("Button", "<Gamepad>/b")
|
||||
.With("Modifier", "<Keyboard>/leftAlt")
|
||||
.With("Button", "<Keyboard>/backspace");
|
||||
|
||||
var next = debugActionMap.AddAction(kDebugNextBtn, type: InputActionType.Button);
|
||||
next.AddBinding("<Keyboard>/pageDown");
|
||||
next.AddBinding("<Gamepad>/rightShoulder");
|
||||
|
||||
var previous = debugActionMap.AddAction(kDebugPreviousBtn, type: InputActionType.Button);
|
||||
previous.AddBinding("<Keyboard>/pageUp");
|
||||
previous.AddBinding("<Gamepad>/leftShoulder");
|
||||
|
||||
var validateAction = debugActionMap.AddAction(kValidateBtn, type: InputActionType.Button);
|
||||
validateAction.AddBinding("<Keyboard>/enter");
|
||||
validateAction.AddBinding("<Gamepad>/a");
|
||||
|
||||
var persistentAction = debugActionMap.AddAction(kPersistentBtn, type: InputActionType.Button);
|
||||
persistentAction.AddBinding("<Keyboard>/rightShift");
|
||||
persistentAction.AddBinding("<Gamepad>/x");
|
||||
|
||||
var multiplierAction = debugActionMap.AddAction(kMultiplierBtn, type: InputActionType.Value);
|
||||
multiplierAction.AddBinding("<Keyboard>/leftShift");
|
||||
multiplierAction.AddBinding("<Gamepad>/y");
|
||||
|
||||
var moveVerticalAction = debugActionMap.AddAction(kDPadVertical);
|
||||
moveVerticalAction.AddCompositeBinding("1DAxis")
|
||||
.With("Positive", "<Gamepad>/dpad/up")
|
||||
.With("Negative", "<Gamepad>/dpad/down")
|
||||
.With("Positive", "<Keyboard>/upArrow")
|
||||
.With("Negative", "<Keyboard>/downArrow");
|
||||
|
||||
var moveHorizontalAction = debugActionMap.AddAction(kDPadHorizontal);
|
||||
moveHorizontalAction.AddCompositeBinding("1DAxis")
|
||||
.With("Positive", "<Gamepad>/dpad/right")
|
||||
.With("Negative", "<Gamepad>/dpad/left")
|
||||
.With("Positive", "<Keyboard>/rightArrow")
|
||||
.With("Negative", "<Keyboard>/leftArrow");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
class DebugActionDesc
|
||||
{
|
||||
#if USE_INPUT_SYSTEM
|
||||
public InputAction buttonAction = null;
|
||||
#else
|
||||
public string axisTrigger = "";
|
||||
public List<string[]> buttonTriggerList = new List<string[]>();
|
||||
public List<KeyCode[]> keyTriggerList = new List<KeyCode[]>();
|
||||
#endif
|
||||
public DebugActionRepeatMode repeatMode = DebugActionRepeatMode.Never;
|
||||
public float repeatDelay;
|
||||
}
|
||||
|
||||
class DebugActionState
|
||||
{
|
||||
enum DebugActionKeyType
|
||||
{
|
||||
Button,
|
||||
Axis,
|
||||
Key
|
||||
}
|
||||
|
||||
DebugActionKeyType m_Type;
|
||||
#if USE_INPUT_SYSTEM
|
||||
InputAction inputAction;
|
||||
#else
|
||||
string[] m_PressedButtons;
|
||||
string m_PressedAxis = "";
|
||||
KeyCode[] m_PressedKeys;
|
||||
#endif
|
||||
bool[] m_TriggerPressedUp;
|
||||
float m_Timer;
|
||||
|
||||
internal bool runningAction { get; private set; }
|
||||
internal float actionState { get; private set; }
|
||||
|
||||
void Trigger(int triggerCount, float state)
|
||||
{
|
||||
actionState = state;
|
||||
runningAction = true;
|
||||
m_Timer = 0f;
|
||||
|
||||
m_TriggerPressedUp = new bool[triggerCount];
|
||||
for (int i = 0; i < m_TriggerPressedUp.Length; ++i)
|
||||
m_TriggerPressedUp[i] = false;
|
||||
}
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
public void TriggerWithButton(InputAction action, float state)
|
||||
{
|
||||
inputAction = action;
|
||||
Trigger(action.bindings.Count, state);
|
||||
}
|
||||
|
||||
#else
|
||||
public void TriggerWithButton(string[] buttons, float state)
|
||||
{
|
||||
m_Type = DebugActionKeyType.Button;
|
||||
m_PressedButtons = buttons;
|
||||
m_PressedAxis = "";
|
||||
Trigger(buttons.Length, state);
|
||||
}
|
||||
|
||||
public void TriggerWithAxis(string axis, float state)
|
||||
{
|
||||
m_Type = DebugActionKeyType.Axis;
|
||||
m_PressedAxis = axis;
|
||||
Trigger(1, state);
|
||||
}
|
||||
|
||||
public void TriggerWithKey(KeyCode[] keys, float state)
|
||||
{
|
||||
m_Type = DebugActionKeyType.Key;
|
||||
m_PressedKeys = keys;
|
||||
m_PressedAxis = "";
|
||||
Trigger(keys.Length, state);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Reset()
|
||||
{
|
||||
runningAction = false;
|
||||
m_Timer = 0f;
|
||||
m_TriggerPressedUp = null;
|
||||
}
|
||||
|
||||
public void Update(DebugActionDesc desc)
|
||||
{
|
||||
// Always reset this so that the action can only be caught once until repeat/reset
|
||||
actionState = 0f;
|
||||
|
||||
if (m_TriggerPressedUp != null)
|
||||
{
|
||||
m_Timer += Time.deltaTime;
|
||||
|
||||
for (int i = 0; i < m_TriggerPressedUp.Length; ++i)
|
||||
{
|
||||
#if USE_INPUT_SYSTEM
|
||||
if (inputAction != null)
|
||||
m_TriggerPressedUp[i] |= Mathf.Approximately(inputAction.ReadValue<float>(), 0f);
|
||||
#else
|
||||
if (m_Type == DebugActionKeyType.Button)
|
||||
m_TriggerPressedUp[i] |= Input.GetButtonUp(m_PressedButtons[i]);
|
||||
else if (m_Type == DebugActionKeyType.Axis)
|
||||
m_TriggerPressedUp[i] |= Mathf.Approximately(Input.GetAxis(m_PressedAxis), 0f);
|
||||
else
|
||||
m_TriggerPressedUp[i] |= Input.GetKeyUp(m_PressedKeys[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool allTriggerUp = true;
|
||||
foreach (bool value in m_TriggerPressedUp)
|
||||
allTriggerUp &= value;
|
||||
|
||||
if (allTriggerUp || (m_Timer > desc.repeatDelay && desc.repeatMode == DebugActionRepeatMode.Delay))
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b9ea0a15a47dead4ebc52bd3f7a926c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,430 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Rendering.UI;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
/// <summary>
|
||||
/// IDebugData interface.
|
||||
/// </summary>
|
||||
public interface IDebugData
|
||||
{
|
||||
/// <summary>Get the reset callback for this DebugData</summary>
|
||||
/// <returns>The reset callback</returns>
|
||||
Action GetReset();
|
||||
//Action GetLoad();
|
||||
//Action GetSave();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manager class for the Debug Window.
|
||||
/// </summary>
|
||||
public sealed partial class DebugManager
|
||||
{
|
||||
static readonly Lazy<DebugManager> s_Instance = new Lazy<DebugManager>(() => new DebugManager());
|
||||
/// <summary>
|
||||
/// Global instance of the DebugManager.
|
||||
/// </summary>
|
||||
public static DebugManager instance => s_Instance.Value;
|
||||
|
||||
ReadOnlyCollection<DebugUI.Panel> m_ReadOnlyPanels;
|
||||
readonly List<DebugUI.Panel> m_Panels = new List<DebugUI.Panel>();
|
||||
|
||||
void UpdateReadOnlyCollection()
|
||||
{
|
||||
m_Panels.Sort();
|
||||
m_ReadOnlyPanels = m_Panels.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of currently registered debug panels.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<DebugUI.Panel> panels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ReadOnlyPanels == null)
|
||||
UpdateReadOnlyCollection();
|
||||
return m_ReadOnlyPanels;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback called when the runtime UI changed.
|
||||
/// </summary>
|
||||
public event Action<bool> onDisplayRuntimeUIChanged = delegate { };
|
||||
/// <summary>
|
||||
/// Callback called when the debug window is dirty.
|
||||
/// </summary>
|
||||
public event Action onSetDirty = delegate { };
|
||||
|
||||
event Action resetData;
|
||||
|
||||
/// <summary>
|
||||
/// Force an editor request.
|
||||
/// </summary>
|
||||
public bool refreshEditorRequested;
|
||||
|
||||
int? m_RequestedPanelIndex;
|
||||
|
||||
GameObject m_Root;
|
||||
DebugUIHandlerCanvas m_RootUICanvas;
|
||||
|
||||
GameObject m_PersistentRoot;
|
||||
DebugUIHandlerPersistentCanvas m_RootUIPersistentCanvas;
|
||||
|
||||
// Knowing if the DebugWindows is open, is done by event as it is in another assembly.
|
||||
// The DebugWindows is responsible to link its event to ToggleEditorUI.
|
||||
bool m_EditorOpen = false;
|
||||
/// <summary>
|
||||
/// Is the debug editor window open.
|
||||
/// </summary>
|
||||
public bool displayEditorUI => m_EditorOpen;
|
||||
/// <summary>
|
||||
/// Toggle the debug window.
|
||||
/// </summary>
|
||||
/// <param name="open">State of the debug window.</param>
|
||||
public void ToggleEditorUI(bool open) => m_EditorOpen = open;
|
||||
|
||||
private bool m_EnableRuntimeUI = true;
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether runtime UI can be enabled. When this is set to false, there will be no overhead
|
||||
/// from debug GameObjects or runtime initialization.
|
||||
/// </summary>
|
||||
public bool enableRuntimeUI
|
||||
{
|
||||
get => m_EnableRuntimeUI;
|
||||
set
|
||||
{
|
||||
if (value != m_EnableRuntimeUI)
|
||||
{
|
||||
m_EnableRuntimeUI = value;
|
||||
DebugUpdater.SetEnabled(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the runtime version of the debug window.
|
||||
/// </summary>
|
||||
public bool displayRuntimeUI
|
||||
{
|
||||
get => m_Root != null && m_Root.activeInHierarchy;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
m_Root = UnityObject.Instantiate(Resources.Load<Transform>("DebugUICanvas")).gameObject;
|
||||
m_Root.name = "[Debug Canvas]";
|
||||
m_Root.transform.localPosition = Vector3.zero;
|
||||
m_RootUICanvas = m_Root.GetComponent<DebugUIHandlerCanvas>();
|
||||
|
||||
#if UNITY_ANDROID || UNITY_IPHONE || UNITY_TVOS || UNITY_SWITCH
|
||||
var canvasScaler = m_Root.GetComponent<CanvasScaler>();
|
||||
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
#endif
|
||||
|
||||
m_Root.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
CoreUtils.Destroy(m_Root);
|
||||
m_Root = null;
|
||||
m_RootUICanvas = null;
|
||||
}
|
||||
|
||||
onDisplayRuntimeUIChanged(value);
|
||||
DebugUpdater.HandleInternalEventSystemComponents(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the persistent runtime debug window.
|
||||
/// </summary>
|
||||
public bool displayPersistentRuntimeUI
|
||||
{
|
||||
get => m_RootUIPersistentCanvas != null && m_PersistentRoot.activeInHierarchy;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
EnsurePersistentCanvas();
|
||||
}
|
||||
else
|
||||
{
|
||||
CoreUtils.Destroy(m_PersistentRoot);
|
||||
m_PersistentRoot = null;
|
||||
m_RootUIPersistentCanvas = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugManager()
|
||||
{
|
||||
if (!Debug.isDebugBuild)
|
||||
return;
|
||||
|
||||
RegisterInputs();
|
||||
RegisterActions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the debug window.
|
||||
/// </summary>
|
||||
public void RefreshEditor()
|
||||
{
|
||||
refreshEditorRequested = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the debug window.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
resetData?.Invoke();
|
||||
ReDrawOnScreenDebug();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request the runtime debug UI be redrawn on the next update.
|
||||
/// </summary>
|
||||
public void ReDrawOnScreenDebug()
|
||||
{
|
||||
if (displayRuntimeUI)
|
||||
m_RootUICanvas?.RequestHierarchyReset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register debug data.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to be registered.</param>
|
||||
public void RegisterData(IDebugData data) => resetData += data.GetReset();
|
||||
|
||||
/// <summary>
|
||||
/// Register debug data.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to be registered.</param>
|
||||
public void UnregisterData(IDebugData data) => resetData -= data.GetReset();
|
||||
|
||||
/// <summary>
|
||||
/// Get hashcode state of the Debug Window.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetState()
|
||||
{
|
||||
int hash = 17;
|
||||
|
||||
foreach (var panel in m_Panels)
|
||||
hash = hash * 23 + panel.GetHashCode();
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
internal void RegisterRootCanvas(DebugUIHandlerCanvas root)
|
||||
{
|
||||
Assert.IsNotNull(root);
|
||||
m_Root = root.gameObject;
|
||||
m_RootUICanvas = root;
|
||||
}
|
||||
|
||||
internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext)
|
||||
{
|
||||
m_RootUICanvas.ChangeSelection(widget, fromNext);
|
||||
}
|
||||
|
||||
internal void SetScrollTarget(DebugUIHandlerWidget widget)
|
||||
{
|
||||
if (m_RootUICanvas != null)
|
||||
m_RootUICanvas.SetScrollTarget(widget);
|
||||
}
|
||||
|
||||
void EnsurePersistentCanvas()
|
||||
{
|
||||
if (m_RootUIPersistentCanvas == null)
|
||||
{
|
||||
var uiManager = UnityObject.FindObjectOfType<DebugUIHandlerPersistentCanvas>();
|
||||
|
||||
if (uiManager == null)
|
||||
{
|
||||
m_PersistentRoot = UnityObject.Instantiate(Resources.Load<Transform>("DebugUIPersistentCanvas")).gameObject;
|
||||
m_PersistentRoot.name = "[Debug Canvas - Persistent]";
|
||||
m_PersistentRoot.transform.localPosition = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PersistentRoot = uiManager.gameObject;
|
||||
}
|
||||
|
||||
m_RootUIPersistentCanvas = m_PersistentRoot.GetComponent<DebugUIHandlerPersistentCanvas>();
|
||||
}
|
||||
}
|
||||
|
||||
internal void TogglePersistent(DebugUI.Widget widget)
|
||||
{
|
||||
if (widget == null)
|
||||
return;
|
||||
|
||||
var valueWidget = widget as DebugUI.Value;
|
||||
if (valueWidget == null)
|
||||
{
|
||||
Debug.Log("Only DebugUI.Value items can be made persistent.");
|
||||
return;
|
||||
}
|
||||
|
||||
EnsurePersistentCanvas();
|
||||
m_RootUIPersistentCanvas.Toggle(valueWidget);
|
||||
}
|
||||
|
||||
void OnPanelDirty(DebugUI.Panel panel)
|
||||
{
|
||||
onSetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request DebugWindow to open the specified panel.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the debug window panel to activate.</param>
|
||||
public void RequestEditorWindowPanelIndex(int index)
|
||||
{
|
||||
// Similar to RefreshEditor(), this function is required to bypass a dependency problem where DebugWindow
|
||||
// cannot be accessed from the Core.Runtime assembly. Should there be a better way to allow editor-dependent
|
||||
// features in DebugUI?
|
||||
m_RequestedPanelIndex = index;
|
||||
}
|
||||
|
||||
internal int? GetRequestedEditorWindowPanelIndex()
|
||||
{
|
||||
int? requestedIndex = m_RequestedPanelIndex;
|
||||
m_RequestedPanelIndex = null;
|
||||
return requestedIndex;
|
||||
}
|
||||
|
||||
// TODO: Optimally we should use a query path here instead of a display name
|
||||
/// <summary>
|
||||
/// Returns a debug panel.
|
||||
/// </summary>
|
||||
/// <param name="displayName">Name of the debug panel.</param>
|
||||
/// <param name="createIfNull">Create the panel if it does not exists.</param>
|
||||
/// <param name="groupIndex">Group index.</param>
|
||||
/// <param name="overrideIfExist">Replace an existing panel.</param>
|
||||
/// <returns></returns>
|
||||
public DebugUI.Panel GetPanel(string displayName, bool createIfNull = false, int groupIndex = 0, bool overrideIfExist = false)
|
||||
{
|
||||
DebugUI.Panel p = null;
|
||||
|
||||
foreach (var panel in m_Panels)
|
||||
{
|
||||
if (panel.displayName == displayName)
|
||||
{
|
||||
p = panel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
if (overrideIfExist)
|
||||
{
|
||||
p.onSetDirty -= OnPanelDirty;
|
||||
RemovePanel(p);
|
||||
p = null;
|
||||
}
|
||||
else
|
||||
return p;
|
||||
}
|
||||
|
||||
if (createIfNull)
|
||||
{
|
||||
p = new DebugUI.Panel { displayName = displayName, groupIndex = groupIndex };
|
||||
p.onSetDirty += OnPanelDirty;
|
||||
m_Panels.Add(p);
|
||||
UpdateReadOnlyCollection();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// TODO: Use a query path here as well instead of a display name
|
||||
/// <summary>
|
||||
/// Remove a debug panel.
|
||||
/// </summary>
|
||||
/// <param name="displayName">Name of the debug panel to remove.</param>
|
||||
public void RemovePanel(string displayName)
|
||||
{
|
||||
DebugUI.Panel panel = null;
|
||||
|
||||
foreach (var p in m_Panels)
|
||||
{
|
||||
if (p.displayName == displayName)
|
||||
{
|
||||
p.onSetDirty -= OnPanelDirty;
|
||||
panel = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RemovePanel(panel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a debug panel.
|
||||
/// </summary>
|
||||
/// <param name="panel">Reference to the debug panel to remove.</param>
|
||||
public void RemovePanel(DebugUI.Panel panel)
|
||||
{
|
||||
if (panel == null)
|
||||
return;
|
||||
|
||||
m_Panels.Remove(panel);
|
||||
UpdateReadOnlyCollection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Debug Item.
|
||||
/// </summary>
|
||||
/// <param name="queryPath">Path of the debug item.</param>
|
||||
/// <returns>Reference to the requested debug item.</returns>
|
||||
public DebugUI.Widget GetItem(string queryPath)
|
||||
{
|
||||
foreach (var panel in m_Panels)
|
||||
{
|
||||
var w = GetItem(queryPath, panel);
|
||||
if (w != null)
|
||||
return w;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a debug item from a specific container.
|
||||
/// </summary>
|
||||
/// <param name="queryPath">Path of the debug item.</param>
|
||||
/// <param name="container">Container to query.</param>
|
||||
/// <returns>Reference to the requested debug item.</returns>
|
||||
DebugUI.Widget GetItem(string queryPath, DebugUI.IContainer container)
|
||||
{
|
||||
foreach (var child in container.children)
|
||||
{
|
||||
if (child.queryPath == queryPath)
|
||||
return child;
|
||||
|
||||
var containerChild = child as DebugUI.IContainer;
|
||||
if (containerChild != null)
|
||||
{
|
||||
var w = GetItem(queryPath, containerChild);
|
||||
if (w != null)
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a09c907f481d4894a96697283a87c352
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,516 @@
|
|||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>Debug class containing several debug shapes for debugging</summary>
|
||||
public partial class DebugShapes
|
||||
{
|
||||
// Singleton
|
||||
static DebugShapes s_Instance = null;
|
||||
|
||||
/// <summary>Singleton instance</summary>
|
||||
static public DebugShapes instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
{
|
||||
s_Instance = new DebugShapes();
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
Mesh m_sphereMesh = null;
|
||||
Mesh m_boxMesh = null;
|
||||
Mesh m_coneMesh = null;
|
||||
Mesh m_pyramidMesh = null;
|
||||
|
||||
// This code has been grabbed from http://wiki.unity3d.com/index.php/ProceduralPrimitives
|
||||
void BuildSphere(ref Mesh outputMesh, float radius, uint longSubdiv, uint latSubdiv)
|
||||
{
|
||||
// Make sure it is empty before pushing anything to it
|
||||
outputMesh.Clear();
|
||||
|
||||
// Build the vertices array
|
||||
Vector3[] vertices = new Vector3[(longSubdiv + 1) * latSubdiv + 2];
|
||||
float _pi = Mathf.PI;
|
||||
float _2pi = _pi * 2f;
|
||||
|
||||
vertices[0] = Vector3.up * radius;
|
||||
for (int lat = 0; lat < latSubdiv; lat++)
|
||||
{
|
||||
float a1 = _pi * (float)(lat + 1) / (latSubdiv + 1);
|
||||
float sin1 = Mathf.Sin(a1);
|
||||
float cos1 = Mathf.Cos(a1);
|
||||
|
||||
for (int lon = 0; lon <= longSubdiv; lon++)
|
||||
{
|
||||
float a2 = _2pi * (float)(lon == longSubdiv ? 0 : lon) / longSubdiv;
|
||||
float sin2 = Mathf.Sin(a2);
|
||||
float cos2 = Mathf.Cos(a2);
|
||||
|
||||
vertices[lon + lat * (longSubdiv + 1) + 1] = new Vector3(sin1 * cos2, cos1, sin1 * sin2) * radius;
|
||||
}
|
||||
}
|
||||
vertices[vertices.Length - 1] = Vector3.up * -radius;
|
||||
|
||||
// Build the normals array
|
||||
Vector3[] normals = new Vector3[vertices.Length];
|
||||
for (int n = 0; n < vertices.Length; n++)
|
||||
{
|
||||
normals[n] = vertices[n].normalized;
|
||||
}
|
||||
|
||||
// Build the UV array
|
||||
Vector2[] uvs = new Vector2[vertices.Length];
|
||||
uvs[0] = Vector2.up;
|
||||
uvs[uvs.Length - 1] = Vector2.zero;
|
||||
for (int lat = 0; lat < latSubdiv; lat++)
|
||||
{
|
||||
for (int lon = 0; lon <= longSubdiv; lon++)
|
||||
{
|
||||
uvs[lon + lat * (longSubdiv + 1) + 1] = new Vector2((float)lon / longSubdiv, 1f - (float)(lat + 1) / (latSubdiv + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Build the index array
|
||||
int nbFaces = vertices.Length;
|
||||
int nbTriangles = nbFaces * 2;
|
||||
int nbIndexes = nbTriangles * 3;
|
||||
int[] triangles = new int[nbIndexes];
|
||||
|
||||
// Top Cap
|
||||
int i = 0;
|
||||
for (int lon = 0; lon < longSubdiv; lon++)
|
||||
{
|
||||
triangles[i++] = lon + 2;
|
||||
triangles[i++] = lon + 1;
|
||||
triangles[i++] = 0;
|
||||
}
|
||||
|
||||
//Middle
|
||||
for (uint lat = 0; lat < latSubdiv - 1; lat++)
|
||||
{
|
||||
for (uint lon = 0; lon < longSubdiv; lon++)
|
||||
{
|
||||
uint current = lon + lat * (longSubdiv + 1) + 1;
|
||||
uint next = current + longSubdiv + 1;
|
||||
|
||||
triangles[i++] = (int)current;
|
||||
triangles[i++] = (int)current + 1;
|
||||
triangles[i++] = (int)next + 1;
|
||||
|
||||
triangles[i++] = (int)current;
|
||||
triangles[i++] = (int)next + 1;
|
||||
triangles[i++] = (int)next;
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom Cap
|
||||
for (int lon = 0; lon < longSubdiv; lon++)
|
||||
{
|
||||
triangles[i++] = vertices.Length - 1;
|
||||
triangles[i++] = vertices.Length - (lon + 2) - 1;
|
||||
triangles[i++] = vertices.Length - (lon + 1) - 1;
|
||||
}
|
||||
|
||||
// Assign them to
|
||||
outputMesh.vertices = vertices;
|
||||
outputMesh.normals = normals;
|
||||
outputMesh.uv = uvs;
|
||||
outputMesh.triangles = triangles;
|
||||
|
||||
outputMesh.RecalculateBounds();
|
||||
}
|
||||
|
||||
void BuildBox(ref Mesh outputMesh, float length, float width, float height)
|
||||
{
|
||||
outputMesh.Clear();
|
||||
|
||||
Vector3 p0 = new Vector3(-length * .5f, -width * .5f, height * .5f);
|
||||
Vector3 p1 = new Vector3(length * .5f, -width * .5f, height * .5f);
|
||||
Vector3 p2 = new Vector3(length * .5f, -width * .5f, -height * .5f);
|
||||
Vector3 p3 = new Vector3(-length * .5f, -width * .5f, -height * .5f);
|
||||
|
||||
Vector3 p4 = new Vector3(-length * .5f, width * .5f, height * .5f);
|
||||
Vector3 p5 = new Vector3(length * .5f, width * .5f, height * .5f);
|
||||
Vector3 p6 = new Vector3(length * .5f, width * .5f, -height * .5f);
|
||||
Vector3 p7 = new Vector3(-length * .5f, width * .5f, -height * .5f);
|
||||
|
||||
Vector3[] vertices = new Vector3[]
|
||||
{
|
||||
// Bottom
|
||||
p0, p1, p2, p3,
|
||||
// Left
|
||||
p7, p4, p0, p3,
|
||||
// Front
|
||||
p4, p5, p1, p0,
|
||||
// Back
|
||||
p6, p7, p3, p2,
|
||||
// Right
|
||||
p5, p6, p2, p1,
|
||||
// Top
|
||||
p7, p6, p5, p4
|
||||
};
|
||||
|
||||
Vector3 up = Vector3.up;
|
||||
Vector3 down = Vector3.down;
|
||||
Vector3 front = Vector3.forward;
|
||||
Vector3 back = Vector3.back;
|
||||
Vector3 left = Vector3.left;
|
||||
Vector3 right = Vector3.right;
|
||||
|
||||
Vector3[] normales = new Vector3[]
|
||||
{
|
||||
// Bottom
|
||||
down, down, down, down,
|
||||
// Left
|
||||
left, left, left, left,
|
||||
// Front
|
||||
front, front, front, front,
|
||||
// Back
|
||||
back, back, back, back,
|
||||
// Right
|
||||
right, right, right, right,
|
||||
// Top
|
||||
up, up, up, up
|
||||
};
|
||||
|
||||
Vector2 _00 = new Vector2(0f, 0f);
|
||||
Vector2 _10 = new Vector2(1f, 0f);
|
||||
Vector2 _01 = new Vector2(0f, 1f);
|
||||
Vector2 _11 = new Vector2(1f, 1f);
|
||||
|
||||
Vector2[] uvs = new Vector2[]
|
||||
{
|
||||
// Bottom
|
||||
_11, _01, _00, _10,
|
||||
// Left
|
||||
_11, _01, _00, _10,
|
||||
// Front
|
||||
_11, _01, _00, _10,
|
||||
// Back
|
||||
_11, _01, _00, _10,
|
||||
// Right
|
||||
_11, _01, _00, _10,
|
||||
// Top
|
||||
_11, _01, _00, _10,
|
||||
};
|
||||
|
||||
int[] triangles = new int[]
|
||||
{
|
||||
// Bottom
|
||||
3, 1, 0,
|
||||
3, 2, 1,
|
||||
// Left
|
||||
3 + 4 * 1, 1 + 4 * 1, 0 + 4 * 1,
|
||||
3 + 4 * 1, 2 + 4 * 1, 1 + 4 * 1,
|
||||
// Front
|
||||
3 + 4 * 2, 1 + 4 * 2, 0 + 4 * 2,
|
||||
3 + 4 * 2, 2 + 4 * 2, 1 + 4 * 2,
|
||||
// Back
|
||||
3 + 4 * 3, 1 + 4 * 3, 0 + 4 * 3,
|
||||
3 + 4 * 3, 2 + 4 * 3, 1 + 4 * 3,
|
||||
// Right
|
||||
3 + 4 * 4, 1 + 4 * 4, 0 + 4 * 4,
|
||||
3 + 4 * 4, 2 + 4 * 4, 1 + 4 * 4,
|
||||
// Top
|
||||
3 + 4 * 5, 1 + 4 * 5, 0 + 4 * 5,
|
||||
3 + 4 * 5, 2 + 4 * 5, 1 + 4 * 5,
|
||||
};
|
||||
|
||||
outputMesh.vertices = vertices;
|
||||
outputMesh.normals = normales;
|
||||
outputMesh.uv = uvs;
|
||||
outputMesh.triangles = triangles;
|
||||
|
||||
outputMesh.RecalculateBounds();
|
||||
}
|
||||
|
||||
void BuildCone(ref Mesh outputMesh, float height, float topRadius, float bottomRadius, int nbSides)
|
||||
{
|
||||
outputMesh.Clear();
|
||||
|
||||
int nbVerticesCap = nbSides + 1;
|
||||
|
||||
// bottom + top + sides
|
||||
Vector3[] vertices = new Vector3[nbVerticesCap + nbVerticesCap + nbSides * 2 + 2];
|
||||
int vert = 0;
|
||||
float _2pi = Mathf.PI * 2f;
|
||||
|
||||
// Bottom cap
|
||||
vertices[vert++] = new Vector3(0f, 0f, 0f);
|
||||
while (vert <= nbSides)
|
||||
{
|
||||
float rad = (float)vert / nbSides * _2pi;
|
||||
vertices[vert] = new Vector3(Mathf.Sin(rad) * bottomRadius, Mathf.Cos(rad) * bottomRadius, 0f);
|
||||
vert++;
|
||||
}
|
||||
|
||||
// Top cap
|
||||
vertices[vert++] = new Vector3(0f, 0f, height);
|
||||
while (vert <= nbSides * 2 + 1)
|
||||
{
|
||||
float rad = (float)(vert - nbSides - 1) / nbSides * _2pi;
|
||||
vertices[vert] = new Vector3(Mathf.Sin(rad) * topRadius, Mathf.Cos(rad) * topRadius, height);
|
||||
vert++;
|
||||
}
|
||||
|
||||
// Sides
|
||||
int v = 0;
|
||||
while (vert <= vertices.Length - 4)
|
||||
{
|
||||
float rad = (float)v / nbSides * _2pi;
|
||||
vertices[vert] = new Vector3(Mathf.Sin(rad) * topRadius, Mathf.Cos(rad) * topRadius, height);
|
||||
vertices[vert + 1] = new Vector3(Mathf.Sin(rad) * bottomRadius, Mathf.Cos(rad) * bottomRadius, 0);
|
||||
vert += 2;
|
||||
v++;
|
||||
}
|
||||
vertices[vert] = vertices[nbSides * 2 + 2];
|
||||
vertices[vert + 1] = vertices[nbSides * 2 + 3];
|
||||
|
||||
// bottom + top + sides
|
||||
Vector3[] normales = new Vector3[vertices.Length];
|
||||
vert = 0;
|
||||
|
||||
// Bottom cap
|
||||
while (vert <= nbSides)
|
||||
{
|
||||
normales[vert++] = new Vector3(0, 0, -1);
|
||||
}
|
||||
|
||||
// Top cap
|
||||
while (vert <= nbSides * 2 + 1)
|
||||
{
|
||||
normales[vert++] = new Vector3(0, 0, 1);
|
||||
}
|
||||
|
||||
// Sides
|
||||
v = 0;
|
||||
while (vert <= vertices.Length - 4)
|
||||
{
|
||||
float rad = (float)v / nbSides * _2pi;
|
||||
float cos = Mathf.Cos(rad);
|
||||
float sin = Mathf.Sin(rad);
|
||||
|
||||
normales[vert] = new Vector3(sin, cos, 0f);
|
||||
normales[vert + 1] = normales[vert];
|
||||
|
||||
vert += 2;
|
||||
v++;
|
||||
}
|
||||
normales[vert] = normales[nbSides * 2 + 2];
|
||||
normales[vert + 1] = normales[nbSides * 2 + 3];
|
||||
|
||||
Vector2[] uvs = new Vector2[vertices.Length];
|
||||
|
||||
// Bottom cap
|
||||
int u = 0;
|
||||
uvs[u++] = new Vector2(0.5f, 0.5f);
|
||||
while (u <= nbSides)
|
||||
{
|
||||
float rad = (float)u / nbSides * _2pi;
|
||||
uvs[u] = new Vector2(Mathf.Cos(rad) * .5f + .5f, Mathf.Sin(rad) * .5f + .5f);
|
||||
u++;
|
||||
}
|
||||
|
||||
// Top cap
|
||||
uvs[u++] = new Vector2(0.5f, 0.5f);
|
||||
while (u <= nbSides * 2 + 1)
|
||||
{
|
||||
float rad = (float)u / nbSides * _2pi;
|
||||
uvs[u] = new Vector2(Mathf.Cos(rad) * .5f + .5f, Mathf.Sin(rad) * .5f + .5f);
|
||||
u++;
|
||||
}
|
||||
|
||||
// Sides
|
||||
int u_sides = 0;
|
||||
while (u <= uvs.Length - 4)
|
||||
{
|
||||
float t = (float)u_sides / nbSides;
|
||||
uvs[u] = new Vector3(t, 1f);
|
||||
uvs[u + 1] = new Vector3(t, 0f);
|
||||
u += 2;
|
||||
u_sides++;
|
||||
}
|
||||
uvs[u] = new Vector2(1f, 1f);
|
||||
uvs[u + 1] = new Vector2(1f, 0f);
|
||||
|
||||
int nbTriangles = nbSides + nbSides + nbSides * 2;
|
||||
int[] triangles = new int[nbTriangles * 3 + 3];
|
||||
|
||||
// Bottom cap
|
||||
int tri = 0;
|
||||
int i = 0;
|
||||
while (tri < nbSides - 1)
|
||||
{
|
||||
triangles[i] = 0;
|
||||
triangles[i + 1] = tri + 1;
|
||||
triangles[i + 2] = tri + 2;
|
||||
tri++;
|
||||
i += 3;
|
||||
}
|
||||
triangles[i] = 0;
|
||||
triangles[i + 1] = tri + 1;
|
||||
triangles[i + 2] = 1;
|
||||
tri++;
|
||||
i += 3;
|
||||
|
||||
// Top cap
|
||||
//tri++;
|
||||
while (tri < nbSides * 2)
|
||||
{
|
||||
triangles[i] = tri + 2;
|
||||
triangles[i + 1] = tri + 1;
|
||||
triangles[i + 2] = nbVerticesCap;
|
||||
tri++;
|
||||
i += 3;
|
||||
}
|
||||
|
||||
triangles[i] = nbVerticesCap + 1;
|
||||
triangles[i + 1] = tri + 1;
|
||||
triangles[i + 2] = nbVerticesCap;
|
||||
tri++;
|
||||
i += 3;
|
||||
tri++;
|
||||
|
||||
// Sides
|
||||
while (tri <= nbTriangles)
|
||||
{
|
||||
triangles[i] = tri + 2;
|
||||
triangles[i + 1] = tri + 1;
|
||||
triangles[i + 2] = tri + 0;
|
||||
tri++;
|
||||
i += 3;
|
||||
|
||||
triangles[i] = tri + 1;
|
||||
triangles[i + 1] = tri + 2;
|
||||
triangles[i + 2] = tri + 0;
|
||||
tri++;
|
||||
i += 3;
|
||||
}
|
||||
|
||||
outputMesh.vertices = vertices;
|
||||
outputMesh.normals = normales;
|
||||
outputMesh.uv = uvs;
|
||||
outputMesh.triangles = triangles;
|
||||
|
||||
outputMesh.RecalculateBounds();
|
||||
}
|
||||
|
||||
void BuildPyramid(ref Mesh outputMesh, float width, float height, float depth)
|
||||
{
|
||||
outputMesh.Clear();
|
||||
|
||||
// Allocate the buffer
|
||||
Vector3[] vertices = new Vector3[16];
|
||||
|
||||
// Top Face
|
||||
vertices[0] = new Vector3(0f, 0f, 0f);
|
||||
vertices[1] = new Vector3(-width / 2.0f, height / 2.0f, depth);
|
||||
vertices[2] = new Vector3(width / 2.0f, height / 2.0f, depth);
|
||||
|
||||
// Left Face
|
||||
vertices[3] = new Vector3(0f, 0f, 0f);
|
||||
vertices[4] = new Vector3(width / 2.0f, height / 2.0f, depth);
|
||||
vertices[5] = new Vector3(width / 2.0f, -height / 2.0f, depth);
|
||||
|
||||
// Bottom Face
|
||||
vertices[6] = new Vector3(0f, 0f, 0f);
|
||||
vertices[7] = new Vector3(width / 2.0f, -height / 2.0f, depth);
|
||||
vertices[8] = new Vector3(-width / 2.0f, -height / 2.0f, depth);
|
||||
|
||||
// Right Face
|
||||
vertices[9] = new Vector3(0f, 0f, 0f);
|
||||
vertices[10] = new Vector3(-width / 2.0f, -height / 2.0f, depth);
|
||||
vertices[11] = new Vector3(-width / 2.0f, height / 2.0f, depth);
|
||||
|
||||
// Cap
|
||||
vertices[12] = new Vector3(-width / 2.0f, height / 2.0f, depth);
|
||||
vertices[13] = new Vector3(-width / 2.0f, -height / 2.0f, depth);
|
||||
vertices[14] = new Vector3(width / 2.0f, -height / 2.0f, depth);
|
||||
vertices[15] = new Vector3(width / 2.0f, height / 2.0f, depth);
|
||||
|
||||
// TODO: support the uv/normals
|
||||
Vector3[] normals = new Vector3[vertices.Length];
|
||||
Vector2[] uvs = new Vector2[vertices.Length];
|
||||
|
||||
// The indexes for the side part is simple
|
||||
int[] triangles = new int[18];
|
||||
for (int idx = 0; idx < 12; ++idx)
|
||||
{
|
||||
triangles[idx] = idx;
|
||||
}
|
||||
|
||||
// Cap indexes
|
||||
triangles[12] = 12;
|
||||
triangles[13] = 13;
|
||||
triangles[14] = 14;
|
||||
triangles[15] = 12;
|
||||
triangles[16] = 14;
|
||||
triangles[17] = 15;
|
||||
|
||||
outputMesh.vertices = vertices;
|
||||
outputMesh.normals = normals;
|
||||
outputMesh.uv = uvs;
|
||||
outputMesh.triangles = triangles;
|
||||
|
||||
outputMesh.RecalculateBounds();
|
||||
}
|
||||
|
||||
void BuildShapes()
|
||||
{
|
||||
m_sphereMesh = new Mesh();
|
||||
BuildSphere(ref m_sphereMesh, 1.0f, 24, 16);
|
||||
|
||||
m_boxMesh = new Mesh();
|
||||
BuildBox(ref m_boxMesh, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
m_coneMesh = new Mesh();
|
||||
BuildCone(ref m_coneMesh, 1.0f, 1.0f, 0.0f, 16);
|
||||
|
||||
m_pyramidMesh = new Mesh();
|
||||
BuildPyramid(ref m_pyramidMesh, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void RebuildResources()
|
||||
{
|
||||
if (m_sphereMesh == null || m_boxMesh == null || m_coneMesh == null || m_pyramidMesh == null)
|
||||
{
|
||||
BuildShapes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get a Sphere Mesh</summary>
|
||||
/// <returns>A Sphere Mesh</returns>
|
||||
public Mesh RequestSphereMesh()
|
||||
{
|
||||
RebuildResources();
|
||||
return m_sphereMesh;
|
||||
}
|
||||
|
||||
/// <summary>Get a Box Mesh</summary>
|
||||
/// <returns>A Box Mesh</returns>
|
||||
public Mesh RequestBoxMesh()
|
||||
{
|
||||
RebuildResources();
|
||||
return m_boxMesh;
|
||||
}
|
||||
|
||||
/// <summary>Get a Cone Mesh</summary>
|
||||
/// <returns>A Cone Mesh</returns>
|
||||
public Mesh RequestConeMesh()
|
||||
{
|
||||
RebuildResources();
|
||||
return m_coneMesh;
|
||||
}
|
||||
|
||||
/// <summary>Get a Pyramid Mesh</summary>
|
||||
/// <returns>A Pyramid Mesh</returns>
|
||||
public Mesh RequestPyramidMesh()
|
||||
{
|
||||
RebuildResources();
|
||||
return m_pyramidMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a164ae4d75dc0074b966a2efdf0a5700
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
public partial class DebugUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for "container" type widgets, although it can be used on its own (if a display name is set then it'll behave as a group with a header)
|
||||
/// </summary>
|
||||
public class Container : Widget, IContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// List of children.
|
||||
/// </summary>
|
||||
public ObservableList<Widget> children { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Panel the container is attached to.
|
||||
/// </summary>
|
||||
public override Panel panel
|
||||
{
|
||||
get { return m_Panel; }
|
||||
internal set
|
||||
{
|
||||
m_Panel = value;
|
||||
|
||||
// Bubble down
|
||||
foreach (var child in children)
|
||||
child.panel = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public Container()
|
||||
{
|
||||
displayName = "";
|
||||
children = new ObservableList<Widget>();
|
||||
children.ItemAdded += OnItemAdded;
|
||||
children.ItemRemoved += OnItemRemoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="displayName">Display name of the container.</param>
|
||||
/// <param name="children">List of attached children.</param>
|
||||
public Container(string displayName, ObservableList<Widget> children)
|
||||
{
|
||||
this.displayName = displayName;
|
||||
this.children = children;
|
||||
children.ItemAdded += OnItemAdded;
|
||||
children.ItemRemoved += OnItemRemoved;
|
||||
}
|
||||
|
||||
internal override void GenerateQueryPath()
|
||||
{
|
||||
base.GenerateQueryPath();
|
||||
|
||||
foreach (var child in children)
|
||||
child.GenerateQueryPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called when a children is added.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender widget.</param>
|
||||
/// <param name="e">List of added children.</param>
|
||||
protected virtual void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
|
||||
{
|
||||
if (e.item != null)
|
||||
{
|
||||
e.item.panel = m_Panel;
|
||||
e.item.parent = this;
|
||||
}
|
||||
|
||||
if (m_Panel != null)
|
||||
m_Panel.SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called when a children is removed.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender widget.</param>
|
||||
/// <param name="e">List of removed children.</param>
|
||||
protected virtual void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
|
||||
{
|
||||
if (e.item != null)
|
||||
{
|
||||
e.item.panel = null;
|
||||
e.item.parent = null;
|
||||
}
|
||||
|
||||
if (m_Panel != null)
|
||||
m_Panel.SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code of the widget.
|
||||
/// </summary>
|
||||
/// <returns>Hash code of the widget.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + queryPath.GetHashCode();
|
||||
|
||||
foreach (var child in children)
|
||||
hash = hash * 23 + child.GetHashCode();
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity-like foldout that can be collapsed.
|
||||
/// </summary>
|
||||
public class Foldout : Container, IValueField
|
||||
{
|
||||
/// <summary>
|
||||
/// Context menu item.
|
||||
/// </summary>
|
||||
public struct ContextMenuItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the item displayed in context menu dropdown.
|
||||
/// </summary>
|
||||
public string displayName;
|
||||
|
||||
/// <summary>
|
||||
/// Callback when context menu item is selected.
|
||||
/// </summary>
|
||||
public Action action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always false.
|
||||
/// </summary>
|
||||
public bool isReadOnly { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Opened state of the foldout.
|
||||
/// </summary>
|
||||
public bool opened;
|
||||
|
||||
/// <summary>
|
||||
/// Draw the foldout in full width using a header style.
|
||||
/// </summary>
|
||||
public bool isHeader;
|
||||
|
||||
/// <summary>
|
||||
/// Optional list of context menu items. If the list is not provided, no context menu button will be displayed.
|
||||
/// </summary>
|
||||
public List<ContextMenuItem> contextMenuItems = null;
|
||||
|
||||
/// <summary>
|
||||
/// List of columns labels.
|
||||
/// </summary>
|
||||
public string[] columnLabels { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// List of columns label tooltips.
|
||||
/// </summary>
|
||||
public string[] columnTooltips { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Foldout() : base() { }
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="displayName">Display name of the foldout.</param>
|
||||
/// <param name="children">List of attached children.</param>
|
||||
/// <param name="columnLabels">Optional list of column names.</param>
|
||||
/// <param name="columnTooltips">Optional list of tooltips for column name labels.</param>
|
||||
public Foldout(string displayName, ObservableList<Widget> children, string[] columnLabels = null, string[] columnTooltips = null)
|
||||
: base(displayName, children)
|
||||
{
|
||||
this.columnLabels = columnLabels;
|
||||
this.columnTooltips = columnTooltips;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the opened state of the foldout.
|
||||
/// </summary>
|
||||
/// <returns>True if the foldout is opened.</returns>
|
||||
public bool GetValue() => opened;
|
||||
|
||||
/// <summary>
|
||||
/// Get the opened state of the foldout.
|
||||
/// </summary>
|
||||
/// <returns>True if the foldout is opened.</returns>
|
||||
object IValueField.GetValue() => GetValue();
|
||||
|
||||
/// <summary>
|
||||
/// Set the opened state of the foldout.
|
||||
/// </summary>
|
||||
/// <param name="value">True to open the foldout, false to close it.</param>
|
||||
public void SetValue(object value) => SetValue((bool)value);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the value of the widget before setting it.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>The validated value.</returns>
|
||||
public object ValidateValue(object value) => value;
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the widget.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
public void SetValue(bool value) => opened = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal Layout Container.
|
||||
/// </summary>
|
||||
public class HBox : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public HBox()
|
||||
{
|
||||
displayName = "HBox";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vertical Layout Container.
|
||||
/// </summary>
|
||||
public class VBox : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public VBox()
|
||||
{
|
||||
displayName = "VBox";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Array Container.
|
||||
/// </summary>
|
||||
public class Table : Container
|
||||
{
|
||||
/// <summary>Row Container.</summary>
|
||||
public class Row : Foldout
|
||||
{
|
||||
/// <summary>Constructor.</summary>
|
||||
public Row() { displayName = "Row"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the table is read only.
|
||||
/// </summary>
|
||||
public bool isReadOnly = false;
|
||||
|
||||
/// <summary>Constructor.</summary>
|
||||
public Table() { displayName = "Array"; }
|
||||
|
||||
/// <summary>
|
||||
/// Set column visibility.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the column.</param>
|
||||
/// <param name="visible">True if the column should be visible.</param>
|
||||
public void SetColumnVisibility(int index, bool visible)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var header = Header;
|
||||
if (index < 0 || index >= m_ColumnCount)
|
||||
return;
|
||||
|
||||
index++;
|
||||
if (header.IsColumnVisible(index) != visible)
|
||||
{
|
||||
var newVisibleColumns = new System.Collections.Generic.List<int>(header.state.visibleColumns);
|
||||
if (newVisibleColumns.Contains(index))
|
||||
{
|
||||
newVisibleColumns.Remove(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
newVisibleColumns.Add(index);
|
||||
newVisibleColumns.Sort();
|
||||
}
|
||||
header.state.visibleColumns = newVisibleColumns.ToArray();
|
||||
|
||||
var cols = header.state.columns;
|
||||
for (int i = 0; i < cols.Length; i++)
|
||||
cols[i].width = 50f;
|
||||
header.ResizeToFit();
|
||||
}
|
||||
#else
|
||||
var columns = VisibleColumns;
|
||||
if (index < 0 || index > columns.Length)
|
||||
return;
|
||||
|
||||
columns[index] = visible;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get column visibility.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the column.</param>
|
||||
/// <returns>True if the column is visible.</returns>
|
||||
public bool GetColumnVisibility(int index)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var header = Header;
|
||||
if (index < 0 || index >= m_ColumnCount)
|
||||
return false;
|
||||
|
||||
return header.IsColumnVisible(index + 1);
|
||||
#else
|
||||
var columns = VisibleColumns;
|
||||
if (index < 0 || index > columns.Length)
|
||||
return false;
|
||||
|
||||
return columns[index];
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// The scroll position of the table.
|
||||
/// </summary>
|
||||
public Vector2 scroll = Vector2.zero;
|
||||
|
||||
int m_ColumnCount;
|
||||
UnityEditor.IMGUI.Controls.MultiColumnHeader m_Header = null;
|
||||
|
||||
/// <summary>
|
||||
/// The table header for drawing
|
||||
/// </summary>
|
||||
public UnityEditor.IMGUI.Controls.MultiColumnHeader Header
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Header != null)
|
||||
return m_Header;
|
||||
|
||||
if (children.Count != 0)
|
||||
{
|
||||
m_ColumnCount = ((Container)children[0]).children.Count;
|
||||
for (int i = 1; i < children.Count; i++)
|
||||
{
|
||||
if (((Container)children[i]).children.Count != m_ColumnCount)
|
||||
{
|
||||
Debug.LogError("All rows must have the same number of children.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column CreateColumn(string name)
|
||||
{
|
||||
var col = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column()
|
||||
{
|
||||
canSort = false,
|
||||
headerTextAlignment = TextAlignment.Center,
|
||||
headerContent = new GUIContent(name),
|
||||
};
|
||||
|
||||
GUIStyle style = UnityEditor.IMGUI.Controls.MultiColumnHeader.DefaultStyles.columnHeaderCenterAligned;
|
||||
style.CalcMinMaxWidth(col.headerContent, out col.width, out float _);
|
||||
col.width = Mathf.Min(col.width, 50f);
|
||||
return col;
|
||||
}
|
||||
|
||||
var cols = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column[m_ColumnCount + 1];
|
||||
cols[0] = CreateColumn(displayName);
|
||||
cols[0].allowToggleVisibility = false;
|
||||
for (int i = 0; i < m_ColumnCount; i++)
|
||||
cols[i + 1] = CreateColumn(((Container)children[0]).children[i].displayName);
|
||||
|
||||
var state = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState(cols);
|
||||
m_Header = new UnityEditor.IMGUI.Controls.MultiColumnHeader(state) { height = 23 };
|
||||
m_Header.ResizeToFit();
|
||||
return m_Header;
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool[] m_Header = null;
|
||||
|
||||
/// <summary>
|
||||
/// The visible columns
|
||||
/// </summary>
|
||||
public bool[] VisibleColumns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Header != null)
|
||||
return m_Header;
|
||||
|
||||
int columnCount = 0;
|
||||
if (children.Count != 0)
|
||||
{
|
||||
columnCount = ((Container)children[0]).children.Count;
|
||||
for (int i = 1; i < children.Count; i++)
|
||||
{
|
||||
if (((Container)children[i]).children.Count != columnCount)
|
||||
{
|
||||
Debug.LogError("All rows must have the same number of children.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Header = new bool[columnCount];
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
m_Header[i] = true;
|
||||
|
||||
return m_Header;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Method called when a children is added.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender widget.</param>
|
||||
/// <param name="e">List of added children.</param>
|
||||
protected override void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
|
||||
{
|
||||
base.OnItemAdded(sender, e);
|
||||
m_Header = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called when a children is removed.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender widget.</param>
|
||||
/// <param name="e">List of removed children.</param>
|
||||
protected override void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
|
||||
{
|
||||
base.OnItemRemoved(sender, e);
|
||||
m_Header = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 02feae31429679949ab4a4a40c8ad34e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,551 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
public partial class DebugUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic field - will be serialized in the editor if it's not read-only
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class Field<T> : Widget, IValueField
|
||||
{
|
||||
/// <summary>
|
||||
/// Getter for this field.
|
||||
/// </summary>
|
||||
public Func<T> getter { get; set; }
|
||||
/// <summary>
|
||||
/// Setter for this field.
|
||||
/// </summary>
|
||||
public Action<T> setter { get; set; }
|
||||
|
||||
// This should be an `event` but they don't play nice with object initializers in the
|
||||
// version of C# we use.
|
||||
/// <summary>
|
||||
/// Callback used when the value of the field changes.
|
||||
/// </summary>
|
||||
public Action<Field<T>, T> onValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Function used to validate the value when updating the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>Validated value.</returns>
|
||||
object IValueField.ValidateValue(object value)
|
||||
{
|
||||
return ValidateValue((T)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function used to validate the value when updating the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>Validated value.</returns>
|
||||
public virtual T ValidateValue(T value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of the field.
|
||||
/// </summary>
|
||||
/// <returns>Value of the field.</returns>
|
||||
object IValueField.GetValue()
|
||||
{
|
||||
return GetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of the field.
|
||||
/// </summary>
|
||||
/// <returns>Value of the field.</returns>
|
||||
public T GetValue()
|
||||
{
|
||||
Assert.IsNotNull(getter);
|
||||
return getter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
public void SetValue(object value)
|
||||
{
|
||||
SetValue((T)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
public void SetValue(T value)
|
||||
{
|
||||
Assert.IsNotNull(setter);
|
||||
var v = ValidateValue(value);
|
||||
|
||||
if (!v.Equals(getter()))
|
||||
{
|
||||
setter(v);
|
||||
onValueChanged?.Invoke(this, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boolean field.
|
||||
/// </summary>
|
||||
public class BoolField : Field<bool> { }
|
||||
/// <summary>
|
||||
/// Boolean field with history.
|
||||
/// </summary>
|
||||
public class HistoryBoolField : BoolField
|
||||
{
|
||||
/// <summary>
|
||||
/// History getter for this field.
|
||||
/// </summary>
|
||||
public Func<bool>[] historyGetter { get; set; }
|
||||
/// <summary>
|
||||
/// Depth of the field's history.
|
||||
/// </summary>
|
||||
public int historyDepth => historyGetter?.Length ?? 0;
|
||||
/// <summary>
|
||||
/// Get the value of the field at a certain history index.
|
||||
/// </summary>
|
||||
/// <param name="historyIndex">Index of the history to query.</param>
|
||||
/// <returns>Value of the field at the provided history index.</returns>
|
||||
public bool GetHistoryValue(int historyIndex)
|
||||
{
|
||||
Assert.IsNotNull(historyGetter);
|
||||
Assert.IsTrue(historyIndex >= 0 && historyIndex < historyGetter.Length, "out of range historyIndex");
|
||||
Assert.IsNotNull(historyGetter[historyIndex]);
|
||||
return historyGetter[historyIndex]();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integer field.
|
||||
/// </summary>
|
||||
public class IntField : Field<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum value function.
|
||||
/// </summary>
|
||||
public Func<int> min;
|
||||
/// <summary>
|
||||
/// Maximum value function.
|
||||
/// </summary>
|
||||
public Func<int> max;
|
||||
|
||||
// Runtime-only
|
||||
/// <summary>
|
||||
/// Step increment.
|
||||
/// </summary>
|
||||
public int incStep = 1;
|
||||
/// <summary>
|
||||
/// Step increment multiplier.
|
||||
/// </summary>
|
||||
public int intStepMult = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Function used to validate the value when updating the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>Validated value.</returns>
|
||||
public override int ValidateValue(int value)
|
||||
{
|
||||
if (min != null) value = Mathf.Max(value, min());
|
||||
if (max != null) value = Mathf.Min(value, max());
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsigned integer field.
|
||||
/// </summary>
|
||||
public class UIntField : Field<uint>
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum value function.
|
||||
/// </summary>
|
||||
public Func<uint> min;
|
||||
/// <summary>
|
||||
/// Maximum value function.
|
||||
/// </summary>
|
||||
public Func<uint> max;
|
||||
|
||||
// Runtime-only
|
||||
/// <summary>
|
||||
/// Step increment.
|
||||
/// </summary>
|
||||
public uint incStep = 1u;
|
||||
/// <summary>
|
||||
/// Step increment multiplier.
|
||||
/// </summary>
|
||||
public uint intStepMult = 10u;
|
||||
|
||||
/// <summary>
|
||||
/// Function used to validate the value when updating the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>Validated value.</returns>
|
||||
public override uint ValidateValue(uint value)
|
||||
{
|
||||
if (min != null) value = (uint)Mathf.Max((int)value, (int)min());
|
||||
if (max != null) value = (uint)Mathf.Min((int)value, (int)max());
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Float field.
|
||||
/// </summary>
|
||||
public class FloatField : Field<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum value function.
|
||||
/// </summary>
|
||||
public Func<float> min;
|
||||
/// <summary>
|
||||
/// Maximum value function.
|
||||
/// </summary>
|
||||
public Func<float> max;
|
||||
|
||||
// Runtime-only
|
||||
/// <summary>
|
||||
/// Step increment.
|
||||
/// </summary>
|
||||
public float incStep = 0.1f;
|
||||
/// <summary>
|
||||
/// Step increment multiplier.
|
||||
/// </summary>
|
||||
public float incStepMult = 10f;
|
||||
/// <summary>
|
||||
/// Number of decimals.
|
||||
/// </summary>
|
||||
public int decimals = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Function used to validate the value when updating the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>Validated value.</returns>
|
||||
public override float ValidateValue(float value)
|
||||
{
|
||||
if (min != null) value = Mathf.Max(value, min());
|
||||
if (max != null) value = Mathf.Min(value, max());
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static class EnumUtility
|
||||
{
|
||||
internal static GUIContent[] MakeEnumNames(Type enumType)
|
||||
{
|
||||
return enumType.GetFields(BindingFlags.Public | BindingFlags.Static).Select(fieldInfo =>
|
||||
{
|
||||
var description = fieldInfo.GetCustomAttributes(typeof(InspectorNameAttribute), false);
|
||||
|
||||
if (description.Length > 0)
|
||||
{
|
||||
return new GUIContent(((InspectorNameAttribute)description.First()).displayName);
|
||||
}
|
||||
|
||||
// Space-delimit PascalCase (https://stackoverflow.com/questions/155303/net-how-can-you-split-a-caps-delimited-string-into-an-array)
|
||||
var niceName = Regex.Replace(fieldInfo.Name, "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 ");
|
||||
return new GUIContent(niceName);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
internal static int[] MakeEnumValues(Type enumType)
|
||||
{
|
||||
// Linq.Cast<T> on a typeless Array breaks the JIT on PS4/Mono so we have to do it manually
|
||||
//enumValues = Enum.GetValues(value).Cast<int>().ToArray();
|
||||
|
||||
var values = Enum.GetValues(enumType);
|
||||
var enumValues = new int[values.Length];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
enumValues[i] = (int)values.GetValue(i);
|
||||
|
||||
return enumValues;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerator field.
|
||||
/// </summary>
|
||||
public class EnumField : Field<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// List of names of the enumerator entries.
|
||||
/// </summary>
|
||||
public GUIContent[] enumNames;
|
||||
/// <summary>
|
||||
/// List of values of the enumerator entries.
|
||||
/// </summary>
|
||||
public int[] enumValues;
|
||||
|
||||
internal int[] quickSeparators;
|
||||
internal int[] indexes;
|
||||
|
||||
/// <summary>
|
||||
/// Get the enumeration value index.
|
||||
/// </summary>
|
||||
public Func<int> getIndex { get; set; }
|
||||
/// <summary>
|
||||
/// Set the enumeration value index.
|
||||
/// </summary>
|
||||
public Action<int> setIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current enumeration value index.
|
||||
/// </summary>
|
||||
public int currentIndex { get => getIndex(); set => setIndex(value); }
|
||||
|
||||
/// <summary>
|
||||
/// Generates enumerator values and names automatically based on the provided type.
|
||||
/// </summary>
|
||||
public Type autoEnum
|
||||
{
|
||||
set
|
||||
{
|
||||
enumNames = EnumUtility.MakeEnumNames(value);
|
||||
enumValues = EnumUtility.MakeEnumValues(value);
|
||||
InitIndexes();
|
||||
InitQuickSeparators();
|
||||
}
|
||||
}
|
||||
|
||||
internal void InitQuickSeparators()
|
||||
{
|
||||
var enumNamesPrefix = enumNames.Select(x =>
|
||||
{
|
||||
string[] splitted = x.text.Split('/');
|
||||
if (splitted.Length == 1)
|
||||
return "";
|
||||
else
|
||||
return splitted[0];
|
||||
});
|
||||
quickSeparators = new int[enumNamesPrefix.Distinct().Count()];
|
||||
string lastPrefix = null;
|
||||
for (int i = 0, wholeNameIndex = 0; i < quickSeparators.Length; ++i)
|
||||
{
|
||||
var currentTestedPrefix = enumNamesPrefix.ElementAt(wholeNameIndex);
|
||||
while (lastPrefix == currentTestedPrefix)
|
||||
{
|
||||
currentTestedPrefix = enumNamesPrefix.ElementAt(++wholeNameIndex);
|
||||
}
|
||||
lastPrefix = currentTestedPrefix;
|
||||
quickSeparators[i] = wholeNameIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
internal void InitIndexes()
|
||||
{
|
||||
if (enumNames == null)
|
||||
enumNames = new GUIContent[0];
|
||||
|
||||
indexes = new int[enumNames.Length];
|
||||
for (int i = 0; i < enumNames.Length; i++)
|
||||
{
|
||||
indexes[i] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerator field with history.
|
||||
/// </summary>
|
||||
public class HistoryEnumField : EnumField
|
||||
{
|
||||
/// <summary>
|
||||
/// History getter for this field.
|
||||
/// </summary>
|
||||
public Func<int>[] historyIndexGetter { get; set; }
|
||||
/// <summary>
|
||||
/// Depth of the field's history.
|
||||
/// </summary>
|
||||
public int historyDepth => historyIndexGetter?.Length ?? 0;
|
||||
/// <summary>
|
||||
/// Get the value of the field at a certain history index.
|
||||
/// </summary>
|
||||
/// <param name="historyIndex">Index of the history to query.</param>
|
||||
/// <returns>Value of the field at the provided history index.</returns>
|
||||
public int GetHistoryValue(int historyIndex)
|
||||
{
|
||||
Assert.IsNotNull(historyIndexGetter);
|
||||
Assert.IsTrue(historyIndex >= 0 && historyIndex < historyIndexGetter.Length, "out of range historyIndex");
|
||||
Assert.IsNotNull(historyIndexGetter[historyIndex]);
|
||||
return historyIndexGetter[historyIndex]();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitfield enumeration field.
|
||||
/// </summary>
|
||||
public class BitField : Field<Enum>
|
||||
{
|
||||
/// <summary>
|
||||
/// List of names of the enumerator entries.
|
||||
/// </summary>
|
||||
public GUIContent[] enumNames { get; private set; }
|
||||
/// <summary>
|
||||
/// List of values of the enumerator entries.
|
||||
/// </summary>
|
||||
public int[] enumValues { get; private set; }
|
||||
|
||||
Type m_EnumType;
|
||||
|
||||
/// <summary>
|
||||
/// Generates bitfield values and names automatically based on the provided type.
|
||||
/// </summary>
|
||||
public Type enumType
|
||||
{
|
||||
get => m_EnumType;
|
||||
set
|
||||
{
|
||||
m_EnumType = value;
|
||||
enumNames = EnumUtility.MakeEnumNames(value);
|
||||
enumValues = EnumUtility.MakeEnumValues(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color field.
|
||||
/// </summary>
|
||||
public class ColorField : Field<Color>
|
||||
{
|
||||
/// <summary>
|
||||
/// HDR color.
|
||||
/// </summary>
|
||||
public bool hdr = false;
|
||||
/// <summary>
|
||||
/// Show alpha of the color field.
|
||||
/// </summary>
|
||||
public bool showAlpha = true;
|
||||
|
||||
// Editor-only
|
||||
/// <summary>
|
||||
/// Show the color picker.
|
||||
/// </summary>
|
||||
public bool showPicker = true;
|
||||
|
||||
// Runtime-only
|
||||
/// <summary>
|
||||
/// Step increment.
|
||||
/// </summary>
|
||||
public float incStep = 0.025f;
|
||||
/// <summary>
|
||||
/// Step increment multiplier.
|
||||
/// </summary>
|
||||
public float incStepMult = 5f;
|
||||
/// <summary>
|
||||
/// Number of decimals.
|
||||
/// </summary>
|
||||
public int decimals = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Function used to validate the value when updating the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>Validated value.</returns>
|
||||
public override Color ValidateValue(Color value)
|
||||
{
|
||||
if (!hdr)
|
||||
{
|
||||
value.r = Mathf.Clamp01(value.r);
|
||||
value.g = Mathf.Clamp01(value.g);
|
||||
value.b = Mathf.Clamp01(value.b);
|
||||
value.a = Mathf.Clamp01(value.a);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vector2 field.
|
||||
/// </summary>
|
||||
public class Vector2Field : Field<Vector2>
|
||||
{
|
||||
// Runtime-only
|
||||
/// <summary>
|
||||
/// Step increment.
|
||||
/// </summary>
|
||||
public float incStep = 0.025f;
|
||||
/// <summary>
|
||||
/// Step increment multiplier.
|
||||
/// </summary>
|
||||
public float incStepMult = 10f;
|
||||
/// <summary>
|
||||
/// Number of decimals.
|
||||
/// </summary>
|
||||
public int decimals = 3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vector3 field.
|
||||
/// </summary>
|
||||
public class Vector3Field : Field<Vector3>
|
||||
{
|
||||
// Runtime-only
|
||||
/// <summary>
|
||||
/// Step increment.
|
||||
/// </summary>
|
||||
public float incStep = 0.025f;
|
||||
/// <summary>
|
||||
/// Step increment multiplier.
|
||||
/// </summary>
|
||||
public float incStepMult = 10f;
|
||||
/// <summary>
|
||||
/// Number of decimals.
|
||||
/// </summary>
|
||||
public int decimals = 3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vector4 field.
|
||||
/// </summary>
|
||||
public class Vector4Field : Field<Vector4>
|
||||
{
|
||||
// Runtime-only
|
||||
/// <summary>
|
||||
/// Step increment.
|
||||
/// </summary>
|
||||
public float incStep = 0.025f;
|
||||
/// <summary>
|
||||
/// Step increment multiplier.
|
||||
/// </summary>
|
||||
public float incStepMult = 10f;
|
||||
/// <summary>
|
||||
/// Number of decimals.
|
||||
/// </summary>
|
||||
public int decimals = 3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple message box widget, providing a couple of different styles.
|
||||
/// </summary>
|
||||
public class MessageBox : Widget
|
||||
{
|
||||
/// <summary>
|
||||
/// Label style defines text color and background.
|
||||
/// </summary>
|
||||
public enum Style
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Style used to render displayName.
|
||||
/// </summary>
|
||||
public Style style = Style.Info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 332920c028664644e9e4e1bf5a1631d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
public partial class DebugUI
|
||||
{
|
||||
// Root panel class - we don't want to extend Container here because we need a clear
|
||||
// separation between debug panels and actual widgets
|
||||
/// <summary>
|
||||
/// Root panel class.
|
||||
/// </summary>
|
||||
public class Panel : IContainer, IComparable<Panel>
|
||||
{
|
||||
/// <summary>
|
||||
/// Widget flags for this panel.
|
||||
/// </summary>
|
||||
public Flags flags { get; set; }
|
||||
/// <summary>
|
||||
/// Display name of the panel.
|
||||
/// </summary>
|
||||
public string displayName { get; set; }
|
||||
/// <summary>
|
||||
/// Group index of the panel.
|
||||
/// </summary>
|
||||
public int groupIndex { get; set; }
|
||||
/// <summary>
|
||||
/// Path of the panel.
|
||||
/// </summary>
|
||||
public string queryPath { get { return displayName; } }
|
||||
|
||||
/// <summary>
|
||||
/// Specify if the panel is editor only.
|
||||
/// </summary>
|
||||
public bool isEditorOnly { get { return (flags & Flags.EditorOnly) != 0; } }
|
||||
/// <summary>
|
||||
/// Specify if the panel is runtime only.
|
||||
/// </summary>
|
||||
public bool isRuntimeOnly { get { return (flags & Flags.RuntimeOnly) != 0; } }
|
||||
/// <summary>
|
||||
/// Returns true if the panel is inactive in the editor.
|
||||
/// </summary>
|
||||
public bool isInactiveInEditor { get { return (isRuntimeOnly && !Application.isPlaying); } }
|
||||
/// <summary>
|
||||
/// Returns true if the panel should always be updated.
|
||||
/// </summary>
|
||||
public bool editorForceUpdate { get { return (flags & Flags.EditorForceUpdate) != 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// List of children.
|
||||
/// </summary>
|
||||
public ObservableList<Widget> children { get; private set; }
|
||||
/// <summary>
|
||||
/// Callback used when the panel is set dirty.
|
||||
/// </summary>
|
||||
public event Action<Panel> onSetDirty = delegate { };
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Panel()
|
||||
{
|
||||
children = new ObservableList<Widget>();
|
||||
children.ItemAdded += OnItemAdded;
|
||||
children.ItemRemoved += OnItemRemoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback used when a child is added.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender widget.</param>
|
||||
/// <param name="e">List of added children.</param>
|
||||
protected virtual void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
|
||||
{
|
||||
if (e.item != null)
|
||||
{
|
||||
e.item.panel = this;
|
||||
e.item.parent = this;
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback used when a child is removed.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender widget.</param>
|
||||
/// <param name="e">List of removed children.</param>
|
||||
protected virtual void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
|
||||
{
|
||||
if (e.item != null)
|
||||
{
|
||||
e.item.panel = null;
|
||||
e.item.parent = null;
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the panel dirty.
|
||||
/// </summary>
|
||||
public void SetDirty()
|
||||
{
|
||||
foreach (var child in children)
|
||||
child.GenerateQueryPath();
|
||||
|
||||
onSetDirty(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code of the panel.
|
||||
/// </summary>
|
||||
/// <returns>Hash code of the panel.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + displayName.GetHashCode();
|
||||
|
||||
foreach (var child in children)
|
||||
hash = hash * 23 + child.GetHashCode();
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Comparison function.
|
||||
/// </summary>
|
||||
/// <param name="other">Panel to compare to.</param>
|
||||
/// <returns>True if the panels share the same group index.</returns>
|
||||
int IComparable<Panel>.CompareTo(Panel other) => other == null ? 1 : groupIndex.CompareTo(other.groupIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9a33c6b643214f847b2e0cf1721a64db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
using System;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug UI Class
|
||||
/// </summary>
|
||||
public partial class DebugUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags for Debug UI widgets.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Flags
|
||||
{
|
||||
/// <summary>
|
||||
/// None.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// This widget is Editor only.
|
||||
/// </summary>
|
||||
EditorOnly = 1 << 1,
|
||||
/// <summary>
|
||||
/// This widget is Runtime only.
|
||||
/// </summary>
|
||||
RuntimeOnly = 1 << 2,
|
||||
/// <summary>
|
||||
/// This widget will force the Debug Editor Window refresh.
|
||||
/// </summary>
|
||||
EditorForceUpdate = 1 << 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all debug UI widgets.
|
||||
/// </summary>
|
||||
public abstract class Widget
|
||||
{
|
||||
// Set to null until it's added to a panel, be careful
|
||||
/// <summary>
|
||||
/// Panels containing the widget.
|
||||
/// </summary>
|
||||
protected Panel m_Panel;
|
||||
|
||||
/// <summary>
|
||||
/// Panels containing the widget.
|
||||
/// </summary>
|
||||
public virtual Panel panel
|
||||
{
|
||||
get { return m_Panel; }
|
||||
internal set { m_Panel = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parent container.
|
||||
/// </summary>
|
||||
protected IContainer m_Parent;
|
||||
|
||||
/// <summary>
|
||||
/// Parent container.
|
||||
/// </summary>
|
||||
public virtual IContainer parent
|
||||
{
|
||||
get { return m_Parent; }
|
||||
internal set { m_Parent = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags for the widget.
|
||||
/// </summary>
|
||||
public Flags flags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Display name.
|
||||
/// </summary>
|
||||
public string displayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tooltip.
|
||||
/// </summary>
|
||||
public string tooltip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path of the widget.
|
||||
/// </summary>
|
||||
public string queryPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the widget is Editor only.
|
||||
/// </summary>
|
||||
public bool isEditorOnly => flags.HasFlag(Flags.EditorOnly);
|
||||
|
||||
/// <summary>
|
||||
/// True if the widget is Runtime only.
|
||||
/// </summary>
|
||||
public bool isRuntimeOnly => flags.HasFlag(Flags.RuntimeOnly);
|
||||
|
||||
/// <summary>
|
||||
/// True if the widget is inactive in the editor (i.e. widget is runtime only and the application is not 'Playing').
|
||||
/// </summary>
|
||||
public bool isInactiveInEditor => (isRuntimeOnly && !Application.isPlaying);
|
||||
|
||||
/// <summary>
|
||||
/// Optional delegate that can be used to conditionally hide widgets at runtime (e.g. due to state of other widgets).
|
||||
/// </summary>
|
||||
public Func<bool> isHiddenCallback;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="isHiddenCallback">shouldHideDelegate</see> has been set and returns true, the widget is hidden from the UI.
|
||||
/// </summary>
|
||||
public bool isHidden => isHiddenCallback?.Invoke() ?? false;
|
||||
|
||||
internal virtual void GenerateQueryPath()
|
||||
{
|
||||
queryPath = displayName.Trim();
|
||||
|
||||
if (m_Parent != null)
|
||||
queryPath = m_Parent.queryPath + " -> " + queryPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code of the widget.
|
||||
/// </summary>
|
||||
/// <returns>The hash code of the widget.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return queryPath.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper struct to allow more compact initialization of widgets.
|
||||
/// </summary>
|
||||
public struct NameAndTooltip
|
||||
{
|
||||
public string name;
|
||||
public string tooltip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper setter to allow more compact initialization of widgets.
|
||||
/// </summary>
|
||||
public NameAndTooltip nameAndTooltip
|
||||
{
|
||||
set
|
||||
{
|
||||
displayName = value.name;
|
||||
tooltip = value.tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for widgets that can contain other widgets.
|
||||
/// </summary>
|
||||
public interface IContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// List of children of the container.
|
||||
/// </summary>
|
||||
ObservableList<Widget> children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Display name of the container.
|
||||
/// </summary>
|
||||
string displayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path of the container.
|
||||
/// </summary>
|
||||
string queryPath { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Any widget that implements this will be considered for serialization (only if the setter is set and thus is not read-only)
|
||||
/// </summary>
|
||||
public interface IValueField
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the value of the field.
|
||||
/// </summary>
|
||||
/// <returns>Value of the field.</returns>
|
||||
object GetValue();
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the field.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
void SetValue(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Function used to validate the value when setting it.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
object ValidateValue(object value);
|
||||
}
|
||||
|
||||
// Miscellaneous
|
||||
/// <summary>
|
||||
/// Button widget.
|
||||
/// </summary>
|
||||
public class Button : Widget
|
||||
{
|
||||
/// <summary>
|
||||
/// Action performed by the button.
|
||||
/// </summary>
|
||||
public Action action { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read only Value widget.
|
||||
/// </summary>
|
||||
public class Value : Widget
|
||||
{
|
||||
/// <summary>
|
||||
/// Getter for the Value.
|
||||
/// </summary>
|
||||
public Func<object> getter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Refresh rate for the read-only value (runtime only)
|
||||
/// </summary>
|
||||
public float refreshRate = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Value()
|
||||
{
|
||||
displayName = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of the widget.
|
||||
/// </summary>
|
||||
/// <returns>The value of the widget.</returns>
|
||||
public object GetValue()
|
||||
{
|
||||
Assert.IsNotNull(getter);
|
||||
return getter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4cce618ad8a6d934dbdc631f979b3aa6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
|
||||
#define USE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.UI;
|
||||
using UnityEngine.InputSystem.EnhancedTouch;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
class DebugUpdater : MonoBehaviour
|
||||
{
|
||||
static DebugUpdater s_Instance = null;
|
||||
|
||||
ScreenOrientation m_Orientation;
|
||||
bool m_RuntimeUiWasVisibleLastFrame = false;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void RuntimeInit()
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
if (DebugManager.instance.enableRuntimeUI)
|
||||
EnableRuntime();
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void SetEnabled(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
EnableRuntime();
|
||||
else
|
||||
DisableRuntime();
|
||||
}
|
||||
|
||||
static void EnableRuntime()
|
||||
{
|
||||
if (s_Instance != null)
|
||||
return;
|
||||
|
||||
var go = new GameObject { name = "[Debug Updater]" };
|
||||
s_Instance = go.AddComponent<DebugUpdater>();
|
||||
s_Instance.m_Orientation = Screen.orientation;
|
||||
|
||||
DontDestroyOnLoad(go);
|
||||
|
||||
DebugManager.instance.EnableInputActions();
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
EnhancedTouchSupport.Enable();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void DisableRuntime()
|
||||
{
|
||||
DebugManager debugManager = DebugManager.instance;
|
||||
debugManager.displayRuntimeUI = false;
|
||||
debugManager.displayPersistentRuntimeUI = false;
|
||||
|
||||
if (s_Instance != null)
|
||||
{
|
||||
CoreUtils.Destroy(s_Instance.gameObject);
|
||||
s_Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void HandleInternalEventSystemComponents(bool uiEnabled)
|
||||
{
|
||||
if (s_Instance == null)
|
||||
return;
|
||||
|
||||
if (uiEnabled)
|
||||
s_Instance.EnsureExactlyOneEventSystem();
|
||||
else
|
||||
s_Instance.DestroyDebugEventSystem();
|
||||
}
|
||||
|
||||
void EnsureExactlyOneEventSystem()
|
||||
{
|
||||
var eventSystems = FindObjectsOfType<EventSystem>();
|
||||
var debugEventSystem = GetComponent<EventSystem>();
|
||||
|
||||
if (eventSystems.Length > 1 && debugEventSystem != null)
|
||||
{
|
||||
Debug.Log($"More than one EventSystem detected in scene. Destroying EventSystem owned by DebugUpdater.");
|
||||
DestroyDebugEventSystem();
|
||||
}
|
||||
else if (eventSystems.Length == 0)
|
||||
{
|
||||
Debug.Log($"No EventSystem available. Creating a new EventSystem to enable Rendering Debugger runtime UI.");
|
||||
CreateDebugEventSystem();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(DoAfterInputModuleUpdated(CheckInputModuleExists));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator DoAfterInputModuleUpdated(Action action)
|
||||
{
|
||||
// EventSystem.current.currentInputModule is not updated immediately when EventSystem.current changes. It happens
|
||||
// with a delay in EventSystem.Update(), so wait a couple of frames to ensure that has happened.
|
||||
yield return new WaitForEndOfFrame();
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
action.Invoke();
|
||||
}
|
||||
|
||||
void CheckInputModuleExists()
|
||||
{
|
||||
if (EventSystem.current != null && EventSystem.current.currentInputModule == null)
|
||||
{
|
||||
Debug.LogWarning("Found a game object with EventSystem component but no corresponding BaseInputModule component - Debug UI input might not work correctly.");
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
void AssignDefaultActions()
|
||||
{
|
||||
if (EventSystem.current != null && EventSystem.current.currentInputModule is InputSystemUIInputModule inputSystemModule)
|
||||
{
|
||||
// FIXME: In order to activate default input actions in player builds (required for touch input to work),
|
||||
// we need to call InputSystemUIInputModule.AssignDefaultActions() which was added in com.unity.inputsystem@1.1.0-pre.5.
|
||||
// However, there is a problem in InputSystem package version ordering, where it sorts this version as an
|
||||
// older version than it should be. Hence we cannot write a version define to conditionally compile this function call.
|
||||
// Instead, we use reflection to see if the function is there and can be invoked.
|
||||
//
|
||||
// Once com.unity.inputsystem@1.1.0 is available, create an INPUTSYSTEM_1_1_0_OR_GREATER version define and use it
|
||||
// to conditionally call AssignDefaultActions().
|
||||
System.Reflection.MethodInfo assignDefaultActionsMethod = inputSystemModule.GetType().GetMethod("AssignDefaultActions");
|
||||
if (assignDefaultActionsMethod != null)
|
||||
{
|
||||
assignDefaultActionsMethod.Invoke(inputSystemModule, null);
|
||||
}
|
||||
}
|
||||
|
||||
CheckInputModuleExists();
|
||||
}
|
||||
#endif
|
||||
|
||||
void CreateDebugEventSystem()
|
||||
{
|
||||
gameObject.AddComponent<EventSystem>();
|
||||
#if USE_INPUT_SYSTEM
|
||||
gameObject.AddComponent<InputSystemUIInputModule>();
|
||||
StartCoroutine(DoAfterInputModuleUpdated(AssignDefaultActions));
|
||||
#else
|
||||
gameObject.AddComponent<StandaloneInputModule>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DestroyDebugEventSystem()
|
||||
{
|
||||
var eventSystem = GetComponent<EventSystem>();
|
||||
#if USE_INPUT_SYSTEM
|
||||
var inputModule = GetComponent<InputSystemUIInputModule>();
|
||||
if (inputModule)
|
||||
{
|
||||
CoreUtils.Destroy(inputModule);
|
||||
StartCoroutine(DoAfterInputModuleUpdated(AssignDefaultActions));
|
||||
}
|
||||
#else
|
||||
CoreUtils.Destroy(GetComponent<StandaloneInputModule>());
|
||||
CoreUtils.Destroy(GetComponent<BaseInput>());
|
||||
#endif
|
||||
CoreUtils.Destroy(eventSystem);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
DebugManager debugManager = DebugManager.instance;
|
||||
|
||||
// Runtime UI visibility can change i.e. due to scene unload - allow component cleanup in this case.
|
||||
if (m_RuntimeUiWasVisibleLastFrame != debugManager.displayRuntimeUI)
|
||||
{
|
||||
HandleInternalEventSystemComponents(debugManager.displayRuntimeUI);
|
||||
}
|
||||
|
||||
debugManager.UpdateActions();
|
||||
|
||||
if (debugManager.GetAction(DebugAction.EnableDebugMenu) != 0.0f ||
|
||||
debugManager.GetActionToggleDebugMenuWithTouch())
|
||||
{
|
||||
debugManager.displayRuntimeUI = !debugManager.displayRuntimeUI;
|
||||
}
|
||||
|
||||
if (debugManager.displayRuntimeUI)
|
||||
{
|
||||
if (debugManager.GetAction(DebugAction.ResetAll) != 0.0f)
|
||||
debugManager.Reset();
|
||||
|
||||
if (debugManager.GetActionReleaseScrollTarget())
|
||||
debugManager.SetScrollTarget(null); // Allow mouse wheel scroll without causing auto-scroll
|
||||
}
|
||||
|
||||
if (m_Orientation != Screen.orientation)
|
||||
{
|
||||
StartCoroutine(RefreshRuntimeUINextFrame());
|
||||
m_Orientation = Screen.orientation;
|
||||
}
|
||||
|
||||
m_RuntimeUiWasVisibleLastFrame = debugManager.displayRuntimeUI;
|
||||
}
|
||||
|
||||
static IEnumerator RefreshRuntimeUINextFrame()
|
||||
{
|
||||
yield return null; // Defer runtime UI refresh to next frame to allow canvas to update first.
|
||||
DebugManager.instance.ReDrawOnScreenDebug();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6fa749f6d8ed4b441af9aa005fe0f895
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
|
||||
#define USE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
#endif
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides mouse position for debugging purpose.
|
||||
/// </summary>
|
||||
public class MousePositionDebug
|
||||
{
|
||||
// Singleton
|
||||
private static MousePositionDebug s_Instance = null;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance.
|
||||
/// </summary>
|
||||
static public MousePositionDebug instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
{
|
||||
s_Instance = new MousePositionDebug();
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[ExecuteAlways]
|
||||
class GameViewEventCatcher : MonoBehaviour
|
||||
{
|
||||
public static GameViewEventCatcher s_Instance = null;
|
||||
public static void Cleanup()
|
||||
{
|
||||
if (s_Instance != null)
|
||||
{
|
||||
// Either we call DestroyImmediate or Destroy we get an error :(
|
||||
// GameViewEventCatcher is only use for SSR debugging currently so comment this code and uncomment it if you want to debug SSR
|
||||
//DestroyImmediate(s_Instance.gameObject);
|
||||
//Destroy(s_Instance.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Build()
|
||||
{
|
||||
Cleanup();
|
||||
var go = new GameObject("__GameViewEventCatcher");
|
||||
go.hideFlags = HideFlags.HideAndDontSave;
|
||||
s_Instance = go.AddComponent<GameViewEventCatcher>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Vector2 mousePosition;
|
||||
bool rightClickPressed = false;
|
||||
bool endKeyPressed = false;
|
||||
|
||||
#if USE_INPUT_SYSTEM
|
||||
mousePosition = Pointer.current != null ? Pointer.current.position.ReadValue() : new Vector2(-1, -1);
|
||||
if (Mouse.current != null)
|
||||
rightClickPressed = Mouse.current.rightButton.isPressed;
|
||||
if (Keyboard.current != null)
|
||||
endKeyPressed = Keyboard.current.endKey.isPressed;
|
||||
#else
|
||||
mousePosition = Input.mousePosition;
|
||||
rightClickPressed = Input.GetMouseButton(1);
|
||||
endKeyPressed = Input.GetKey(KeyCode.End);
|
||||
#endif
|
||||
|
||||
if (mousePosition.x < 0
|
||||
|| mousePosition.y < 0
|
||||
|| mousePosition.x > Screen.width
|
||||
|| mousePosition.y > Screen.height)
|
||||
return;
|
||||
|
||||
instance.m_mousePosition = mousePosition;
|
||||
instance.m_mousePosition.y = Screen.height - instance.m_mousePosition.y;
|
||||
if (rightClickPressed)
|
||||
instance.m_MouseClickPosition = instance.m_mousePosition;
|
||||
if (endKeyPressed)
|
||||
instance.m_MouseClickPosition = instance.m_mousePosition;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 m_mousePosition = Vector2.zero;
|
||||
Vector2 m_MouseClickPosition = Vector2.zero;
|
||||
|
||||
private void OnSceneGUI(UnityEditor.SceneView sceneview)
|
||||
{
|
||||
m_mousePosition = Event.current.mousePosition;
|
||||
switch (Event.current.type)
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
m_MouseClickPosition = m_mousePosition;
|
||||
break;
|
||||
case EventType.KeyDown:
|
||||
switch (Event.current.keyCode)
|
||||
{
|
||||
case KeyCode.End:
|
||||
// Usefull we you don't want to change the scene viewport but still update the mouse click position
|
||||
m_MouseClickPosition = m_mousePosition;
|
||||
sceneview.Repaint();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the MousePositionDebug class.
|
||||
/// </summary>
|
||||
public void Build()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.SceneView.duringSceneGui -= OnSceneGUI;
|
||||
UnityEditor.SceneView.duringSceneGui += OnSceneGUI;
|
||||
// Disabled as it cause error: GameViewEventCatcher is only use for SSR debugging currently so comment this code and uncomment it if you want to debug SSR
|
||||
//GameViewEventCatcher.Build();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup the MousePositionDebug class.
|
||||
/// </summary>
|
||||
public void Cleanup()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.SceneView.duringSceneGui -= OnSceneGUI;
|
||||
// Disabled as it cause error: GameViewEventCatcher is only use for SSR debugging currently so comment this code and uncomment it if you want to debug SSR
|
||||
//GameViewEventCatcher.Cleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the mouse position in the scene or game view.
|
||||
/// </summary>
|
||||
/// <param name="ScreenHeight">Window height.</param>
|
||||
/// <param name="sceneView">Get position in the scene view?</param>
|
||||
/// <returns>Coordinates of the mouse in the specified window.</returns>
|
||||
public Vector2 GetMousePosition(float ScreenHeight, bool sceneView)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (sceneView)
|
||||
{
|
||||
// In play mode, m_mousePosition the one in the scene view
|
||||
Vector2 mousePixelCoord = m_mousePosition;
|
||||
mousePixelCoord.y = (ScreenHeight - 1.0f) - mousePixelCoord.y;
|
||||
return mousePixelCoord;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In play mode, Input.mousecoords matches the position in the game view
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
return GetInputMousePosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
// In non-play mode, only m_mousePosition is valid.
|
||||
// We force -1, -1 as a game view pixel pos to avoid
|
||||
// rendering un-wanted effects
|
||||
return new Vector2(-1.0f, -1.0f);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// In app mode, we only use the Input.mousecoords
|
||||
return GetInputMousePosition();
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector2 GetInputMousePosition()
|
||||
{
|
||||
#if USE_INPUT_SYSTEM
|
||||
return Pointer.current != null ? Pointer.current.position.ReadValue() : new Vector2(-1, -1);
|
||||
#else
|
||||
return Input.mousePosition;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the position of the mouse click.
|
||||
/// </summary>
|
||||
/// <param name="ScreenHeight">Window height.</param>
|
||||
/// <returns>The coordinates of the mouse click.</returns>
|
||||
public Vector2 GetMouseClickPosition(float ScreenHeight)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Vector2 mousePixelCoord = m_MouseClickPosition;
|
||||
mousePixelCoord.y = (ScreenHeight - 1.0f) - mousePixelCoord.y;
|
||||
return mousePixelCoord;
|
||||
#else
|
||||
return Vector2.zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 16870bb28557a8f4182d1c8191e951a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d2160913896effa4aa65b93012ff3465
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a11be56b627f5034bbca389c76bd3964
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
|
@ -0,0 +1,22 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 74a5091d8707f334b9a5c31ef71a64ba
|
||||
TrueTypeFontImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
fontSize: 16
|
||||
forceTextureCase: -2
|
||||
characterSpacing: 0
|
||||
characterPadding: 1
|
||||
includeFontData: 1
|
||||
fontName: Perfect DOS VGA 437
|
||||
fontNames:
|
||||
- Perfect DOS VGA 437
|
||||
fallbackFontReferences: []
|
||||
customCharacters:
|
||||
fontRenderingMode: 2
|
||||
ascentCalculationMode: 1
|
||||
useLegacyBoundsCalculation: 0
|
||||
shouldRoundAdvanceValue: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3774a3e29f7c59445ba79c15769126fd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1153602445894428
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 224711363741255626}
|
||||
- component: {fileID: 223912878945851142}
|
||||
- component: {fileID: 114908889885781782}
|
||||
- component: {fileID: 114649910605725082}
|
||||
- component: {fileID: 114530362809716058}
|
||||
m_Layer: 5
|
||||
m_Name: DebugUICanvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &224711363741255626
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1153602445894428}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!223 &223912878945851142
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1153602445894428}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 1
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &114908889885781782
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1153602445894428}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!114 &114649910605725082
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1153602445894428}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &114530362809716058
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1153602445894428}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76db615e524a19c4990482d75a475543, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
panelPrefab: {fileID: 224481716535368988, guid: daa46a58178a6ad41ae1ddc2dc7f856d, type: 3}
|
||||
prefabs:
|
||||
- type: UnityEngine.Rendering.DebugUI+Value, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224720214277421396, guid: dc0f88987826e6e48b1fe9c7c2b53a53, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+BoolField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224131888606727344, guid: ce347ad101f41ee4ab5c3fbc0ea447db, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+IntField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224720214277421396, guid: ae00bb75e0cd5b04b8fe7fb4ab662629, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+UIntField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224720214277421396, guid: f22bcc84a5f4a1944b075a2c4ac71493, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+FloatField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224720214277421396, guid: d8c744701b43c864b88e7f8144e19bc5, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+EnumField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224224135738715566, guid: 988db55689193434fb0b3b89538f978f, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Button, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224438017010656346, guid: f6ce33b91f6ffe54cadacbf4bb112440, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Foldout, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224053494956566916, guid: 1c87ab2ce8b8b304d98fbe9a734b1f74, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+ColorField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224636372931965878, guid: 77c185820dd1a464eac89cae3abccddf, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Vector2Field, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224169904409585018, guid: 326f7c58aed965d41bf7805a782d1e44, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Vector3Field, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224119945032119512, guid: 94afea5f242d72547979595ba963f335, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Vector4Field, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224325631027038092, guid: d47f009476100f545971a81ede14c750, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+VBox, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224489511352681190, guid: ca3e294656861a64b8aeeb9f916da0a9, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+HBox, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224719784157228276, guid: f7f5e36797cf0c1408561665c67b179b, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Container, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224284813447651300, guid: 38a07789c9e87004dad98c2909f58369, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+BitField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 5833802642077810669, guid: 7c78b588b2e1f7c4a86ca4a985cf6e4a, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+HistoryBoolField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 108402283379224504, guid: 5088d0220f0c4df439cf06c5c270eacb, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+HistoryEnumField, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 8535926254376877601, guid: b2da6b27df236b144b3516ed8e7d36ac, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Table, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224284813447651300, guid: 38a07789c9e87004dad98c2909f58369, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+Table+Row, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224053494956566916, guid: 2d019437ff89b8d44949727731cd9357, type: 3}
|
||||
- type: UnityEngine.Rendering.DebugUI+MessageBox, Unity.RenderPipelines.Core.Runtime,
|
||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
prefab: {fileID: 224053494956566916, guid: 10a25524b0986f9488b430e2829bbbe8, type: 3}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cf6cbdd672089a84796e55a21fed1cbe
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 100100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1388241697787146
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 224499400523491650}
|
||||
- component: {fileID: 222203031975944290}
|
||||
- component: {fileID: 114530022413994304}
|
||||
- component: {fileID: 114399612179518328}
|
||||
- component: {fileID: 114307594989265542}
|
||||
m_Layer: 5
|
||||
m_Name: DebugUI Persistent Panel
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &224499400523491650
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388241697787146}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 224556897823040040}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 1, y: 1}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: -5, y: -5}
|
||||
m_SizeDelta: {x: 400, y: 0}
|
||||
m_Pivot: {x: 1, y: 1}
|
||||
--- !u!222 &222203031975944290
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388241697787146}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &114530022413994304
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388241697787146}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.1, g: 0.1, b: 0.1, a: 0.8509804}
|
||||
m_RaycastTarget: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
|
||||
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_Sprite: {fileID: 21300000, guid: 127279d577f25ac4ea17dae3782e5074, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &114399612179518328
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388241697787146}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Padding:
|
||||
m_Left: 5
|
||||
m_Right: 0
|
||||
m_Top: 0
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 0
|
||||
m_Spacing: 0
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 1
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 0
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
--- !u!114 &114307594989265542
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388241697787146}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 1
|
||||
--- !u!1 &1822588063230394
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 224556897823040040}
|
||||
- component: {fileID: 223125086719629416}
|
||||
- component: {fileID: 114876729554496680}
|
||||
- component: {fileID: 114213191034542798}
|
||||
- component: {fileID: 114605181728370468}
|
||||
m_Layer: 5
|
||||
m_Name: DebugUI Persistent Canvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &224556897823040040
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1822588063230394}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_Children:
|
||||
- {fileID: 224499400523491650}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!223 &223125086719629416
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1822588063230394}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 1
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &114876729554496680
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1822588063230394}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
--- !u!114 &114213191034542798
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1822588063230394}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &114605181728370468
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1822588063230394}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 87177621604658d4e893f55be7cfae4a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
panel: {fileID: 224499400523491650}
|
||||
valuePrefab: {fileID: 224720214277421396, guid: dc0f88987826e6e48b1fe9c7c2b53a53,
|
||||
type: 3}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6b1a0fe75d5009449cf55ae76220e2b
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 100100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9383aa19796d6e84e8031655b9ac912f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for Bitfield widget. Require the enum to have a None field set to 0 in its values.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerBitField : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the widget.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Value toggle.</summary>
|
||||
public UIFoldout valueToggle;
|
||||
|
||||
/// <summary>Toggles for the bitfield.</summary>
|
||||
public List<DebugUIHandlerIndirectToggle> toggles;
|
||||
|
||||
DebugUI.BitField m_Field;
|
||||
DebugUIHandlerContainer m_Container;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.BitField>();
|
||||
m_Container = GetComponent<DebugUIHandlerContainer>();
|
||||
nameLabel.text = m_Field.displayName;
|
||||
|
||||
int toggleIndex = 0;
|
||||
foreach (var enumName in m_Field.enumNames)
|
||||
{
|
||||
if (toggleIndex >= toggles.Count)
|
||||
continue;
|
||||
|
||||
var toggle = toggles[toggleIndex];
|
||||
toggle.getter = GetValue;
|
||||
toggle.setter = SetValue;
|
||||
toggle.nextUIHandler = toggleIndex < (m_Field.enumNames.Length - 1) ? toggles[toggleIndex + 1] : null;
|
||||
toggle.previousUIHandler = toggleIndex > 0 ? toggles[toggleIndex - 1] : null;
|
||||
toggle.parentUIHandler = this;
|
||||
toggle.index = toggleIndex;
|
||||
toggle.nameLabel.text = enumName.text;
|
||||
toggle.Init();
|
||||
toggleIndex++;
|
||||
}
|
||||
|
||||
// Destroy the remaining toggles outside of the range of the displayed enum.
|
||||
for (; toggleIndex < toggles.Count; ++toggleIndex)
|
||||
{
|
||||
CoreUtils.Destroy(toggles[toggleIndex].gameObject);
|
||||
toggles[toggleIndex] = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetValue(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
// None can't be selected
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to remove 1 to the index because there is the None element on top of
|
||||
// the enum and it doesn't count in the bit field because it's value is 0
|
||||
index--;
|
||||
int intValue = System.Convert.ToInt32(m_Field.GetValue());
|
||||
return (intValue & (1 << index)) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetValue(int index, bool value)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
// None was selected so we reset all the bits to false
|
||||
m_Field.SetValue(System.Enum.ToObject(m_Field.enumType, 0));
|
||||
foreach (var toggle in toggles)
|
||||
{
|
||||
if (toggle?.getter != null)
|
||||
toggle.UpdateValueLabel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int intValue = System.Convert.ToInt32(m_Field.GetValue());
|
||||
if (value)
|
||||
intValue |= m_Field.enumValues[index];
|
||||
else
|
||||
intValue &= ~m_Field.enumValues[index];
|
||||
m_Field.SetValue(System.Enum.ToObject(m_Field.enumType, intValue));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
if (fromNext || valueToggle.isOn == false)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
}
|
||||
else if (valueToggle.isOn)
|
||||
{
|
||||
if (m_Container.IsDirectChild(previous))
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
}
|
||||
else
|
||||
{
|
||||
var lastItem = m_Container.GetLastItem();
|
||||
DebugManager.instance.ChangeSelection(lastItem, false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnIncrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">True if incrementing fast.</param>
|
||||
public override void OnIncrement(bool fast)
|
||||
{
|
||||
valueToggle.isOn = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDecrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">Trye if decrementing fast.</param>
|
||||
public override void OnDecrement(bool fast)
|
||||
{
|
||||
valueToggle.isOn = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAction implementation.
|
||||
/// </summary>
|
||||
public override void OnAction()
|
||||
{
|
||||
valueToggle.isOn = !valueToggle.isOn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Next implementation.
|
||||
/// </summary>
|
||||
/// <returns>Next widget UI handler, parent if there is none.</returns>
|
||||
public override DebugUIHandlerWidget Next()
|
||||
{
|
||||
if (!valueToggle.isOn || m_Container == null)
|
||||
return base.Next();
|
||||
|
||||
var firstChild = m_Container.GetFirstItem();
|
||||
|
||||
if (firstChild == null)
|
||||
return base.Next();
|
||||
|
||||
return firstChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 662c81b0e51f80a4797c39bbd7116f9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for Button widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerButton : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the widget.</summary>
|
||||
public Text nameLabel;
|
||||
DebugUI.Button m_Field;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.Button>();
|
||||
nameLabel.text = m_Field.displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>State of the widget.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAction implementation.
|
||||
/// </summary>
|
||||
public override void OnAction()
|
||||
{
|
||||
if (m_Field.action != null)
|
||||
m_Field.action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8bff080b4e3bae64c80b54402ced6cc6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug UI Prefab bundle.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class DebugUIPrefabBundle
|
||||
{
|
||||
/// <summary>type of the widget.</summary>
|
||||
public string type;
|
||||
/// <summary>Prefab for the widget.</summary>
|
||||
public RectTransform prefab;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DebugUIHandler for canvas widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerCanvas : MonoBehaviour
|
||||
{
|
||||
int m_DebugTreeState;
|
||||
Dictionary<Type, Transform> m_PrefabsMap;
|
||||
|
||||
/// <summary>Panel prefab.</summary>
|
||||
public Transform panelPrefab;
|
||||
/// <summary>List of prefabs.</summary>
|
||||
public List<DebugUIPrefabBundle> prefabs;
|
||||
|
||||
List<DebugUIHandlerPanel> m_UIPanels;
|
||||
int m_SelectedPanel;
|
||||
DebugUIHandlerWidget m_SelectedWidget;
|
||||
string m_CurrentQueryPath;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (prefabs == null)
|
||||
prefabs = new List<DebugUIPrefabBundle>();
|
||||
|
||||
if (m_PrefabsMap == null)
|
||||
m_PrefabsMap = new Dictionary<Type, Transform>();
|
||||
|
||||
if (m_UIPanels == null)
|
||||
m_UIPanels = new List<DebugUIHandlerPanel>();
|
||||
|
||||
DebugManager.instance.RegisterRootCanvas(this);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
int state = DebugManager.instance.GetState();
|
||||
if (m_DebugTreeState != state)
|
||||
{
|
||||
ResetAllHierarchy();
|
||||
}
|
||||
|
||||
HandleInput();
|
||||
|
||||
// Update scroll position in the panel
|
||||
if (m_UIPanels != null && m_SelectedPanel < m_UIPanels.Count && m_UIPanels[m_SelectedPanel] != null)
|
||||
m_UIPanels[m_SelectedPanel].UpdateScroll();
|
||||
}
|
||||
|
||||
internal void RequestHierarchyReset()
|
||||
{
|
||||
m_DebugTreeState = -1;
|
||||
}
|
||||
|
||||
void ResetAllHierarchy()
|
||||
{
|
||||
foreach (Transform t in transform)
|
||||
CoreUtils.Destroy(t.gameObject);
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
void Rebuild()
|
||||
{
|
||||
// Check prefab associations
|
||||
m_PrefabsMap.Clear();
|
||||
foreach (var bundle in prefabs)
|
||||
{
|
||||
var type = Type.GetType(bundle.type);
|
||||
|
||||
if (type != null && bundle.prefab != null)
|
||||
m_PrefabsMap.Add(type, bundle.prefab);
|
||||
}
|
||||
|
||||
m_UIPanels.Clear();
|
||||
|
||||
m_DebugTreeState = DebugManager.instance.GetState();
|
||||
var panels = DebugManager.instance.panels;
|
||||
|
||||
#if UNITY_ANDROID || UNITY_IPHONE
|
||||
// Mobile device safe area
|
||||
Rect parentRect = GetComponentInParent<RectTransform>().rect;
|
||||
float parentWidth = Math.Min(parentRect.width, parentRect.height);
|
||||
float scaleRatio = parentWidth / Math.Min(Screen.height, Screen.width);
|
||||
|
||||
Rect safeAreaRect = Screen.safeArea;
|
||||
Vector2 margin = new Vector2(5, 5);
|
||||
var safeAreaOffsetLeft = safeAreaRect.xMin * scaleRatio;
|
||||
var safeAreaOffsetTop = -safeAreaRect.yMin * scaleRatio;
|
||||
Vector2 safeAreaOffset = new Vector2(safeAreaOffsetLeft, safeAreaOffsetTop) + margin;
|
||||
#endif
|
||||
|
||||
DebugUIHandlerWidget selectedWidget = null;
|
||||
foreach (var panel in panels)
|
||||
{
|
||||
if (panel.isEditorOnly || panel.children.Count(x => !x.isEditorOnly && !x.isHidden) == 0)
|
||||
continue;
|
||||
|
||||
var go = Instantiate(panelPrefab, transform, false).gameObject;
|
||||
go.name = panel.displayName;
|
||||
|
||||
#if UNITY_ANDROID || UNITY_IPHONE
|
||||
RectTransform rectTransform = go.GetComponent<RectTransform>();
|
||||
rectTransform.anchoredPosition = safeAreaOffset;
|
||||
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, safeAreaRect.height * scaleRatio + 2 * safeAreaOffsetTop);
|
||||
#endif
|
||||
|
||||
var uiPanel = go.GetComponent<DebugUIHandlerPanel>();
|
||||
uiPanel.SetPanel(panel);
|
||||
uiPanel.Canvas = this;
|
||||
m_UIPanels.Add(uiPanel);
|
||||
var container = go.GetComponent<DebugUIHandlerContainer>();
|
||||
DebugUIHandlerWidget selected = null;
|
||||
Traverse(panel, container.contentHolder, null, ref selected);
|
||||
|
||||
if (selected != null && selected.GetWidget().queryPath.Contains(panel.queryPath))
|
||||
{
|
||||
selectedWidget = selected;
|
||||
}
|
||||
}
|
||||
|
||||
ActivatePanel(m_SelectedPanel, selectedWidget);
|
||||
}
|
||||
|
||||
void Traverse(DebugUI.IContainer container, Transform parentTransform, DebugUIHandlerWidget parentUIHandler, ref DebugUIHandlerWidget selectedHandler)
|
||||
{
|
||||
DebugUIHandlerWidget previousUIHandler = null;
|
||||
|
||||
for (int i = 0; i < container.children.Count; i++)
|
||||
{
|
||||
var child = container.children[i];
|
||||
|
||||
if (child.isEditorOnly || child.isHidden)
|
||||
continue;
|
||||
|
||||
Transform prefab;
|
||||
|
||||
if (!m_PrefabsMap.TryGetValue(child.GetType(), out prefab))
|
||||
{
|
||||
Debug.LogWarning("DebugUI widget doesn't have a prefab: " + child.GetType());
|
||||
continue;
|
||||
}
|
||||
|
||||
var go = Instantiate(prefab, parentTransform, false).gameObject;
|
||||
go.name = child.displayName;
|
||||
var uiHandler = go.GetComponent<DebugUIHandlerWidget>();
|
||||
|
||||
if (uiHandler == null)
|
||||
{
|
||||
Debug.LogWarning("DebugUI prefab is missing a DebugUIHandler for: " + child.GetType());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(m_CurrentQueryPath) && child.queryPath.Equals(m_CurrentQueryPath))
|
||||
{
|
||||
selectedHandler = uiHandler;
|
||||
}
|
||||
|
||||
if (previousUIHandler != null) previousUIHandler.nextUIHandler = uiHandler;
|
||||
uiHandler.previousUIHandler = previousUIHandler;
|
||||
previousUIHandler = uiHandler;
|
||||
uiHandler.parentUIHandler = parentUIHandler;
|
||||
uiHandler.SetWidget(child);
|
||||
|
||||
var childContainer = go.GetComponent<DebugUIHandlerContainer>();
|
||||
if (childContainer != null && child is DebugUI.IContainer childAsContainer)
|
||||
Traverse(childAsContainer, childContainer.contentHolder, uiHandler, ref selectedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
DebugUIHandlerWidget GetWidgetFromPath(string queryPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(queryPath))
|
||||
return null;
|
||||
|
||||
var panel = m_UIPanels[m_SelectedPanel];
|
||||
|
||||
return panel
|
||||
.GetComponentsInChildren<DebugUIHandlerWidget>()
|
||||
.FirstOrDefault(w => w.GetWidget().queryPath == queryPath);
|
||||
}
|
||||
|
||||
void ActivatePanel(int index, DebugUIHandlerWidget selectedWidget = null)
|
||||
{
|
||||
if (m_UIPanels.Count == 0)
|
||||
return;
|
||||
|
||||
if (index >= m_UIPanels.Count)
|
||||
index = m_UIPanels.Count - 1;
|
||||
|
||||
m_UIPanels.ForEach(p => p.gameObject.SetActive(false));
|
||||
m_UIPanels[index].gameObject.SetActive(true);
|
||||
m_SelectedPanel = index;
|
||||
|
||||
if (selectedWidget == null)
|
||||
selectedWidget = m_UIPanels[index].GetFirstItem();
|
||||
|
||||
ChangeSelection(selectedWidget, true);
|
||||
}
|
||||
|
||||
internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext)
|
||||
{
|
||||
if (widget == null)
|
||||
return;
|
||||
|
||||
if (m_SelectedWidget != null)
|
||||
m_SelectedWidget.OnDeselection();
|
||||
|
||||
var prev = m_SelectedWidget;
|
||||
m_SelectedWidget = widget;
|
||||
SetScrollTarget(widget);
|
||||
|
||||
if (!m_SelectedWidget.OnSelection(fromNext, prev))
|
||||
{
|
||||
if (fromNext)
|
||||
SelectNextItem();
|
||||
else
|
||||
SelectPreviousItem();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_SelectedWidget == null || m_SelectedWidget.GetWidget() == null)
|
||||
m_CurrentQueryPath = string.Empty;
|
||||
else
|
||||
m_CurrentQueryPath = m_SelectedWidget.GetWidget().queryPath;
|
||||
}
|
||||
}
|
||||
|
||||
internal void SelectPreviousItem()
|
||||
{
|
||||
if (m_SelectedWidget == null)
|
||||
return;
|
||||
|
||||
var newSelection = m_SelectedWidget.Previous();
|
||||
|
||||
if (newSelection != null)
|
||||
ChangeSelection(newSelection, false);
|
||||
}
|
||||
|
||||
internal void SelectNextPanel()
|
||||
{
|
||||
int index = m_SelectedPanel + 1;
|
||||
if (index >= m_UIPanels.Count)
|
||||
index = 0;
|
||||
index = Mathf.Clamp(index, 0, m_UIPanels.Count - 1);
|
||||
ActivatePanel(index);
|
||||
}
|
||||
|
||||
internal void SelectPreviousPanel()
|
||||
{
|
||||
int index = m_SelectedPanel - 1;
|
||||
if (index < 0)
|
||||
index = m_UIPanels.Count - 1;
|
||||
index = Mathf.Clamp(index, 0, m_UIPanels.Count - 1);
|
||||
ActivatePanel(index);
|
||||
}
|
||||
|
||||
internal void SelectNextItem()
|
||||
{
|
||||
if (m_SelectedWidget == null)
|
||||
return;
|
||||
|
||||
var newSelection = m_SelectedWidget.Next();
|
||||
|
||||
if (newSelection != null)
|
||||
ChangeSelection(newSelection, true);
|
||||
}
|
||||
|
||||
void ChangeSelectionValue(float multiplier)
|
||||
{
|
||||
if (m_SelectedWidget == null)
|
||||
return;
|
||||
|
||||
bool fast = DebugManager.instance.GetAction(DebugAction.Multiplier) != 0f;
|
||||
|
||||
if (multiplier < 0f)
|
||||
m_SelectedWidget.OnDecrement(fast);
|
||||
else
|
||||
m_SelectedWidget.OnIncrement(fast);
|
||||
}
|
||||
|
||||
void ActivateSelection()
|
||||
{
|
||||
if (m_SelectedWidget == null)
|
||||
return;
|
||||
|
||||
m_SelectedWidget.OnAction();
|
||||
}
|
||||
|
||||
void HandleInput()
|
||||
{
|
||||
if (DebugManager.instance.GetAction(DebugAction.PreviousDebugPanel) != 0f)
|
||||
{
|
||||
SelectPreviousPanel();
|
||||
}
|
||||
|
||||
if (DebugManager.instance.GetAction(DebugAction.NextDebugPanel) != 0f)
|
||||
{
|
||||
SelectNextPanel();
|
||||
}
|
||||
|
||||
if (DebugManager.instance.GetAction(DebugAction.Action) != 0f)
|
||||
ActivateSelection();
|
||||
|
||||
if (DebugManager.instance.GetAction(DebugAction.MakePersistent) != 0f && m_SelectedWidget != null)
|
||||
DebugManager.instance.TogglePersistent(m_SelectedWidget.GetWidget());
|
||||
|
||||
float moveHorizontal = DebugManager.instance.GetAction(DebugAction.MoveHorizontal);
|
||||
if (moveHorizontal != 0f)
|
||||
ChangeSelectionValue(moveHorizontal);
|
||||
|
||||
float moveVertical = DebugManager.instance.GetAction(DebugAction.MoveVertical);
|
||||
if (moveVertical != 0f)
|
||||
{
|
||||
if (moveVertical < 0f)
|
||||
SelectNextItem();
|
||||
else
|
||||
SelectPreviousItem();
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetScrollTarget(DebugUIHandlerWidget widget)
|
||||
{
|
||||
if (m_UIPanels != null && m_SelectedPanel < m_UIPanels.Count && m_UIPanels[m_SelectedPanel] != null)
|
||||
m_UIPanels[m_SelectedPanel].SetScrollTarget(widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 76db615e524a19c4990482d75a475543
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for color widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerColor : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the widget.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>/// <summary>Name of the widget.</summary>alue toggle.</summary>
|
||||
public UIFoldout valueToggle;
|
||||
/// <summary>Color image.</summary>
|
||||
public Image colorImage;
|
||||
|
||||
/// <summary>Red float field.</summary>
|
||||
public DebugUIHandlerIndirectFloatField fieldR;
|
||||
/// <summary>Green float field.</summary>
|
||||
public DebugUIHandlerIndirectFloatField fieldG;
|
||||
/// <summary>Blue float field.</summary>
|
||||
public DebugUIHandlerIndirectFloatField fieldB;
|
||||
/// <summary>Alpha float field.</summary>
|
||||
public DebugUIHandlerIndirectFloatField fieldA;
|
||||
|
||||
DebugUI.ColorField m_Field;
|
||||
DebugUIHandlerContainer m_Container;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.ColorField>();
|
||||
m_Container = GetComponent<DebugUIHandlerContainer>();
|
||||
nameLabel.text = m_Field.displayName;
|
||||
|
||||
fieldR.getter = () => m_Field.GetValue().r;
|
||||
fieldR.setter = x => SetValue(x, r: true);
|
||||
fieldR.nextUIHandler = fieldG;
|
||||
SetupSettings(fieldR);
|
||||
|
||||
fieldG.getter = () => m_Field.GetValue().g;
|
||||
fieldG.setter = x => SetValue(x, g: true);
|
||||
fieldG.previousUIHandler = fieldR;
|
||||
fieldG.nextUIHandler = fieldB;
|
||||
SetupSettings(fieldG);
|
||||
|
||||
fieldB.getter = () => m_Field.GetValue().b;
|
||||
fieldB.setter = x => SetValue(x, b: true);
|
||||
fieldB.previousUIHandler = fieldG;
|
||||
fieldB.nextUIHandler = m_Field.showAlpha ? fieldA : null;
|
||||
SetupSettings(fieldB);
|
||||
|
||||
fieldA.gameObject.SetActive(m_Field.showAlpha);
|
||||
fieldA.getter = () => m_Field.GetValue().a;
|
||||
fieldA.setter = x => SetValue(x, a: true);
|
||||
fieldA.previousUIHandler = fieldB;
|
||||
SetupSettings(fieldA);
|
||||
|
||||
UpdateColor();
|
||||
}
|
||||
|
||||
void SetValue(float x, bool r = false, bool g = false, bool b = false, bool a = false)
|
||||
{
|
||||
var color = m_Field.GetValue();
|
||||
if (r) color.r = x;
|
||||
if (g) color.g = x;
|
||||
if (b) color.b = x;
|
||||
if (a) color.a = x;
|
||||
m_Field.SetValue(color);
|
||||
UpdateColor();
|
||||
}
|
||||
|
||||
void SetupSettings(DebugUIHandlerIndirectFloatField field)
|
||||
{
|
||||
field.parentUIHandler = this;
|
||||
field.incStepGetter = () => m_Field.incStep;
|
||||
field.incStepMultGetter = () => m_Field.incStepMult;
|
||||
field.decimalsGetter = () => m_Field.decimals;
|
||||
field.Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>State of the widget.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
if (fromNext || valueToggle.isOn == false)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
}
|
||||
else if (valueToggle.isOn)
|
||||
{
|
||||
if (m_Container.IsDirectChild(previous))
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
}
|
||||
else
|
||||
{
|
||||
var lastItem = m_Container.GetLastItem();
|
||||
DebugManager.instance.ChangeSelection(lastItem, false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnIncrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">True if incrementing fast.</param>
|
||||
public override void OnIncrement(bool fast)
|
||||
{
|
||||
valueToggle.isOn = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDecrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">Trye if decrementing fast.</param>
|
||||
public override void OnDecrement(bool fast)
|
||||
{
|
||||
valueToggle.isOn = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAction implementation.
|
||||
/// </summary>
|
||||
public override void OnAction()
|
||||
{
|
||||
valueToggle.isOn = !valueToggle.isOn;
|
||||
}
|
||||
|
||||
internal void UpdateColor()
|
||||
{
|
||||
if (colorImage != null)
|
||||
colorImage.color = m_Field.GetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Next implementation.
|
||||
/// </summary>
|
||||
/// <returns>Next child.</returns>
|
||||
public override DebugUIHandlerWidget Next()
|
||||
{
|
||||
if (!valueToggle.isOn || m_Container == null)
|
||||
return base.Next();
|
||||
|
||||
var firstChild = m_Container.GetFirstItem();
|
||||
|
||||
if (firstChild == null)
|
||||
return base.Next();
|
||||
|
||||
return firstChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 590d0a4a23369fe4094bee34430c4b38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for container widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerContainer : MonoBehaviour
|
||||
{
|
||||
/// <summary>Content holder.</summary>
|
||||
[SerializeField]
|
||||
public RectTransform contentHolder;
|
||||
|
||||
internal DebugUIHandlerWidget GetFirstItem()
|
||||
{
|
||||
if (contentHolder.childCount == 0)
|
||||
return null;
|
||||
|
||||
var items = GetActiveChildren();
|
||||
|
||||
if (items.Count == 0)
|
||||
return null;
|
||||
|
||||
return items[0];
|
||||
}
|
||||
|
||||
internal DebugUIHandlerWidget GetLastItem()
|
||||
{
|
||||
if (contentHolder.childCount == 0)
|
||||
return null;
|
||||
|
||||
var items = GetActiveChildren();
|
||||
|
||||
if (items.Count == 0)
|
||||
return null;
|
||||
|
||||
return items[items.Count - 1];
|
||||
}
|
||||
|
||||
internal bool IsDirectChild(DebugUIHandlerWidget widget)
|
||||
{
|
||||
if (contentHolder.childCount == 0)
|
||||
return false;
|
||||
|
||||
return GetActiveChildren()
|
||||
.Count(x => x == widget) > 0;
|
||||
}
|
||||
|
||||
List<DebugUIHandlerWidget> GetActiveChildren()
|
||||
{
|
||||
var list = new List<DebugUIHandlerWidget>();
|
||||
|
||||
foreach (Transform t in contentHolder)
|
||||
{
|
||||
if (!t.gameObject.activeInHierarchy)
|
||||
continue;
|
||||
|
||||
var c = t.GetComponent<DebugUIHandlerWidget>();
|
||||
if (c != null)
|
||||
list.Add(c);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2bd470ffc0c0fe54faddbf8d466bf519
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for enumerator widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerEnumField : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Text displayed for the "next" button.</summary>
|
||||
public Text nextButtonText;
|
||||
/// <summary>Text displayed for the "previous" button.</summary>
|
||||
public Text previousButtonText;
|
||||
/// <summary>Name of the enum field.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Value of the enum field.</summary>
|
||||
public Text valueLabel;
|
||||
|
||||
internal protected DebugUI.EnumField m_Field;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.EnumField>();
|
||||
nameLabel.text = m_Field.displayName;
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>State of the widget.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
if (nextButtonText != null)
|
||||
nextButtonText.color = colorSelected;
|
||||
if (previousButtonText != null)
|
||||
previousButtonText.color = colorSelected;
|
||||
nameLabel.color = colorSelected;
|
||||
valueLabel.color = colorSelected;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
if (nextButtonText != null)
|
||||
nextButtonText.color = colorDefault;
|
||||
if (previousButtonText != null)
|
||||
previousButtonText.color = colorDefault;
|
||||
nameLabel.color = colorDefault;
|
||||
valueLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAction implementation.
|
||||
/// </summary>
|
||||
public override void OnAction()
|
||||
{
|
||||
OnIncrement(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnIncrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">True if incrementing fast.</param>
|
||||
public override void OnIncrement(bool fast)
|
||||
{
|
||||
if (m_Field.enumValues.Length == 0)
|
||||
return;
|
||||
|
||||
var array = m_Field.enumValues;
|
||||
int index = m_Field.currentIndex;
|
||||
|
||||
if (index == array.Length - 1)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fast)
|
||||
{
|
||||
//check if quickSeparators have not been constructed
|
||||
//it is the case when not constructed with autoenum
|
||||
var separators = m_Field.quickSeparators;
|
||||
if (separators == null)
|
||||
{
|
||||
m_Field.InitQuickSeparators();
|
||||
separators = m_Field.quickSeparators;
|
||||
}
|
||||
|
||||
int idxSup = 0;
|
||||
for (; idxSup < separators.Length && index + 1 > separators[idxSup]; ++idxSup) ;
|
||||
if (idxSup == separators.Length)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = separators[idxSup];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_Field.SetValue(array[index]);
|
||||
m_Field.currentIndex = index;
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDecrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">Trye if decrementing fast.</param>
|
||||
public override void OnDecrement(bool fast)
|
||||
{
|
||||
if (m_Field.enumValues.Length == 0)
|
||||
return;
|
||||
|
||||
var array = m_Field.enumValues;
|
||||
int index = m_Field.currentIndex;
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
if (fast)
|
||||
{
|
||||
//check if quickSeparators have not been constructed
|
||||
//it is thecase when not constructed with autoenum
|
||||
var separators = m_Field.quickSeparators;
|
||||
if (separators == null)
|
||||
{
|
||||
m_Field.InitQuickSeparators();
|
||||
separators = m_Field.quickSeparators;
|
||||
}
|
||||
|
||||
index = separators[separators.Length - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
index = array.Length - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fast)
|
||||
{
|
||||
//check if quickSeparators have not been constructed
|
||||
//it is the case when not constructed with autoenum
|
||||
var separators = m_Field.quickSeparators;
|
||||
if (separators == null)
|
||||
{
|
||||
m_Field.InitQuickSeparators();
|
||||
separators = m_Field.quickSeparators;
|
||||
}
|
||||
|
||||
int idxInf = separators.Length - 1;
|
||||
for (; idxInf > 0 && index <= separators[idxInf]; --idxInf) ;
|
||||
index = separators[idxInf];
|
||||
}
|
||||
else
|
||||
{
|
||||
index -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_Field.SetValue(array[index]);
|
||||
m_Field.currentIndex = index;
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the label of the widget.
|
||||
/// </summary>
|
||||
protected virtual void UpdateValueLabel()
|
||||
{
|
||||
int index = m_Field.currentIndex;
|
||||
|
||||
// Fallback just in case, we may be handling sub/sectionned enums here
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
|
||||
string text = m_Field.enumNames[index].text;
|
||||
|
||||
// The UI implementation is tight with space, so let's just truncate the string here if too long.
|
||||
const int maxLength = 26;
|
||||
if (text.Length > maxLength)
|
||||
{
|
||||
text = text.Substring(0, maxLength - 3) + "...";
|
||||
}
|
||||
|
||||
valueLabel.text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0ca07cb82ca30874c849ad6a8be4ce42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System.Collections;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for enum with history widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerEnumHistory : DebugUIHandlerEnumField
|
||||
{
|
||||
Text[] historyValues;
|
||||
const float xDecal = 60f;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
int historyDepth = (widget as DebugUI.HistoryEnumField)?.historyDepth ?? 0;
|
||||
historyValues = new Text[historyDepth];
|
||||
for (int index = 0; index < historyDepth; ++index)
|
||||
{
|
||||
var historyValue = Instantiate(valueLabel, transform);
|
||||
Vector3 pos = historyValue.transform.position;
|
||||
pos.x += (index + 1) * xDecal;
|
||||
historyValue.transform.position = pos;
|
||||
var text = historyValue.GetComponent<Text>();
|
||||
text.color = new Color32(110, 110, 110, 255);
|
||||
historyValues[index] = text;
|
||||
}
|
||||
|
||||
//this call UpdateValueLabel which will rely on historyToggles
|
||||
base.SetWidget(widget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the label of the widget.
|
||||
/// </summary>
|
||||
protected override void UpdateValueLabel()
|
||||
{
|
||||
int index = m_Field.currentIndex;
|
||||
|
||||
// Fallback just in case, we may be handling sub/sectionned enums here
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
|
||||
valueLabel.text = m_Field.enumNames[index].text;
|
||||
|
||||
DebugUI.HistoryEnumField field = m_Field as DebugUI.HistoryEnumField;
|
||||
int historyDepth = field?.historyDepth ?? 0;
|
||||
for (int indexHistory = 0; indexHistory < historyDepth; ++indexHistory)
|
||||
{
|
||||
if (indexHistory < historyValues.Length && historyValues[indexHistory] != null)
|
||||
historyValues[indexHistory].text = field.enumNames[field.GetHistoryValue(indexHistory)].text;
|
||||
}
|
||||
|
||||
if (isActiveAndEnabled)
|
||||
StartCoroutine(RefreshAfterSanitization());
|
||||
}
|
||||
|
||||
IEnumerator RefreshAfterSanitization()
|
||||
{
|
||||
yield return null; //wait one frame
|
||||
m_Field.currentIndex = m_Field.getIndex();
|
||||
valueLabel.text = m_Field.enumNames[m_Field.currentIndex].text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ecc0d22e5e3de604f9e4d3d8997e9122
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for float widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerFloatField : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the enum field.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Value of the enum field.</summary>
|
||||
public Text valueLabel;
|
||||
DebugUI.FloatField m_Field;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.FloatField>();
|
||||
nameLabel.text = m_Field.displayName;
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
valueLabel.color = colorSelected;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
valueLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnIncrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">True if incrementing fast.</param>
|
||||
public override void OnIncrement(bool fast)
|
||||
{
|
||||
ChangeValue(fast, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDecrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">Trye if decrementing fast.</param>
|
||||
public override void OnDecrement(bool fast)
|
||||
{
|
||||
ChangeValue(fast, -1);
|
||||
}
|
||||
|
||||
void ChangeValue(bool fast, float multiplier)
|
||||
{
|
||||
float value = m_Field.GetValue();
|
||||
value += m_Field.incStep * (fast ? m_Field.incStepMult : 1f) * multiplier;
|
||||
m_Field.SetValue(value);
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
void UpdateValueLabel()
|
||||
{
|
||||
valueLabel.text = m_Field.GetValue().ToString("N" + m_Field.decimals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c71addad67814c418e8376c7fabd008
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for foldout widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerFoldout : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the Foldout.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Toggle value of the Foldout.</summary>
|
||||
public UIFoldout valueToggle;
|
||||
|
||||
DebugUI.Foldout m_Field;
|
||||
DebugUIHandlerContainer m_Container;
|
||||
|
||||
const float xDecal = 60f;
|
||||
const float xDecalInit = 230f;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.Foldout>();
|
||||
m_Container = GetComponent<DebugUIHandlerContainer>();
|
||||
nameLabel.text = m_Field.displayName;
|
||||
|
||||
int columnNumber = m_Field.columnLabels?.Length ?? 0;
|
||||
for (int index = 0; index < columnNumber; ++index)
|
||||
{
|
||||
var column = Instantiate(nameLabel.gameObject, GetComponent<DebugUIHandlerContainer>().contentHolder);
|
||||
column.AddComponent<LayoutElement>().ignoreLayout = true;
|
||||
var rectTransform = column.transform as RectTransform;
|
||||
var originalTransform = nameLabel.transform as RectTransform;
|
||||
rectTransform.anchorMax = rectTransform.anchorMin = new Vector2(0, 1);
|
||||
rectTransform.sizeDelta = new Vector2(100, 26);
|
||||
Vector3 pos = originalTransform.anchoredPosition;
|
||||
pos.x += (index + 1) * xDecal + xDecalInit;
|
||||
rectTransform.anchoredPosition = pos;
|
||||
rectTransform.pivot = new Vector2(0, 0.5f);
|
||||
rectTransform.eulerAngles = new Vector3(0, 0, 13);
|
||||
var text = column.GetComponent<Text>();
|
||||
text.fontSize = 15;
|
||||
text.text = m_Field.columnLabels[index];
|
||||
}
|
||||
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
if (fromNext || valueToggle.isOn == false)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
}
|
||||
else if (valueToggle.isOn)
|
||||
{
|
||||
if (m_Container.IsDirectChild(previous))
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
}
|
||||
else
|
||||
{
|
||||
var lastItem = m_Container.GetLastItem();
|
||||
DebugManager.instance.ChangeSelection(lastItem, false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnIncrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">True if incrementing fast.</param>
|
||||
public override void OnIncrement(bool fast)
|
||||
{
|
||||
m_Field.SetValue(true);
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDecrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">Trye if decrementing fast.</param>
|
||||
public override void OnDecrement(bool fast)
|
||||
{
|
||||
m_Field.SetValue(false);
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAction implementation.
|
||||
/// </summary>
|
||||
public override void OnAction()
|
||||
{
|
||||
bool value = !m_Field.GetValue();
|
||||
m_Field.SetValue(value);
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
void UpdateValue()
|
||||
{
|
||||
valueToggle.isOn = m_Field.GetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Next implementation.
|
||||
/// </summary>
|
||||
/// <returns>Next widget UI handler, parent if there is none.</returns>
|
||||
public override DebugUIHandlerWidget Next()
|
||||
{
|
||||
if (!m_Field.GetValue() || m_Container == null)
|
||||
return base.Next();
|
||||
|
||||
var firstChild = m_Container.GetFirstItem();
|
||||
|
||||
if (firstChild == null)
|
||||
return base.Next();
|
||||
|
||||
return firstChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4e5dc9f9bba5df14fbe439bcc4c67063
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for group widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerGroup : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the group.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Header of the group.</summary>
|
||||
public Transform header;
|
||||
DebugUI.Container m_Field;
|
||||
DebugUIHandlerContainer m_Container;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.Container>();
|
||||
m_Container = GetComponent<DebugUIHandlerContainer>();
|
||||
|
||||
if (string.IsNullOrEmpty(m_Field.displayName))
|
||||
header.gameObject.SetActive(false);
|
||||
else
|
||||
nameLabel.text = m_Field.displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
if (!fromNext && !m_Container.IsDirectChild(previous))
|
||||
{
|
||||
var lastItem = m_Container.GetLastItem();
|
||||
DebugManager.instance.ChangeSelection(lastItem, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Next implementation.
|
||||
/// </summary>
|
||||
/// <returns>Next widget UI handler, parent if there is none.</returns>
|
||||
public override DebugUIHandlerWidget Next()
|
||||
{
|
||||
if (m_Container == null)
|
||||
return base.Next();
|
||||
|
||||
var firstChild = m_Container.GetFirstItem();
|
||||
|
||||
if (firstChild == null)
|
||||
return base.Next();
|
||||
|
||||
return firstChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c5d44b76204264d4eb90d4a92b067d7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for Horizontal Layout widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerHBox : DebugUIHandlerWidget
|
||||
{
|
||||
DebugUIHandlerContainer m_Container;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Container = GetComponent<DebugUIHandlerContainer>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
if (!fromNext && !m_Container.IsDirectChild(previous))
|
||||
{
|
||||
var lastItem = m_Container.GetLastItem();
|
||||
DebugManager.instance.ChangeSelection(lastItem, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Next implementation.
|
||||
/// </summary>
|
||||
/// <returns>Next widget UI handler, parent if there is none.</returns>
|
||||
public override DebugUIHandlerWidget Next()
|
||||
{
|
||||
if (m_Container == null)
|
||||
return base.Next();
|
||||
|
||||
var firstChild = m_Container.GetFirstItem();
|
||||
|
||||
if (firstChild == null)
|
||||
return base.Next();
|
||||
|
||||
return firstChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e8261e6f2e5fac44da64da2b23939e9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for indirect float widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerIndirectFloatField : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the indirect float field.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Value of the indirect float field.</summary>
|
||||
public Text valueLabel;
|
||||
|
||||
/// <summary>
|
||||
/// Getter function for this indirect widget.
|
||||
/// </summary>
|
||||
public Func<float> getter;
|
||||
/// <summary>
|
||||
/// Setter function for this indirect widget.
|
||||
/// </summary>
|
||||
public Action<float> setter;
|
||||
|
||||
/// <summary>
|
||||
/// Getter function for the increment step of this indirect widget.
|
||||
/// </summary>
|
||||
public Func<float> incStepGetter;
|
||||
/// <summary>
|
||||
/// Getter function for the increment step multiplier of this indirect widget.
|
||||
/// </summary>
|
||||
public Func<float> incStepMultGetter;
|
||||
/// <summary>
|
||||
/// Getter function for the number of decimals of this indirect widget.
|
||||
/// </summary>
|
||||
public Func<float> decimalsGetter;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the indirect widget.
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
valueLabel.color = colorSelected;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
valueLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnIncrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">True if incrementing fast.</param>
|
||||
public override void OnIncrement(bool fast)
|
||||
{
|
||||
ChangeValue(fast, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDecrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">Trye if decrementing fast.</param>
|
||||
public override void OnDecrement(bool fast)
|
||||
{
|
||||
ChangeValue(fast, -1);
|
||||
}
|
||||
|
||||
void ChangeValue(bool fast, float multiplier)
|
||||
{
|
||||
float value = getter();
|
||||
value += incStepGetter() * (fast ? incStepMultGetter() : 1f) * multiplier;
|
||||
setter(value);
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
void UpdateValueLabel()
|
||||
{
|
||||
if (valueLabel != null)
|
||||
valueLabel.text = getter().ToString("N" + decimalsGetter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c42b71ff0e23e2d4a8a32a2bc85acac0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for indirect toggle widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerIndirectToggle : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>
|
||||
/// Label of the widget.
|
||||
/// </summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Toggle of the toggle field.</summary>
|
||||
public Toggle valueToggle;
|
||||
/// <summary>Checkmark image.</summary>
|
||||
public Image checkmarkImage;
|
||||
|
||||
/// <summary>
|
||||
/// Getter function for this indirect widget.
|
||||
/// </summary>
|
||||
public Func<int, bool> getter;
|
||||
/// <summary>
|
||||
/// Setter function for this indirect widget.
|
||||
/// </summary>
|
||||
public Action<int, bool> setter;
|
||||
|
||||
// Should not be here, this is a byproduct of the Bitfield UI Handler implementation.
|
||||
internal int index;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the indirect widget.
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
UpdateValueLabel();
|
||||
|
||||
valueToggle.onValueChanged.AddListener(OnToggleValueChanged);
|
||||
}
|
||||
|
||||
void OnToggleValueChanged(bool value)
|
||||
{
|
||||
setter(index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
checkmarkImage.color = colorSelected;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
checkmarkImage.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAction implementation.
|
||||
/// </summary>
|
||||
public override void OnAction()
|
||||
{
|
||||
bool value = !getter(index);
|
||||
setter(index, value);
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
internal void UpdateValueLabel()
|
||||
{
|
||||
if (valueToggle != null)
|
||||
valueToggle.isOn = getter(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0012fc4688b5d5342a441aa32c0e099e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.Rendering.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugUIHandler for integer widget.
|
||||
/// </summary>
|
||||
public class DebugUIHandlerIntField : DebugUIHandlerWidget
|
||||
{
|
||||
/// <summary>Name of the int field.</summary>
|
||||
public Text nameLabel;
|
||||
/// <summary>Value of the int field.</summary>
|
||||
public Text valueLabel;
|
||||
DebugUI.IntField m_Field;
|
||||
|
||||
internal override void SetWidget(DebugUI.Widget widget)
|
||||
{
|
||||
base.SetWidget(widget);
|
||||
m_Field = CastWidget<DebugUI.IntField>();
|
||||
nameLabel.text = m_Field.displayName;
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnSelection implementation.
|
||||
/// </summary>
|
||||
/// <param name="fromNext">True if the selection wrapped around.</param>
|
||||
/// <param name="previous">Previous widget.</param>
|
||||
/// <returns>True if the selection is allowed.</returns>
|
||||
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
|
||||
{
|
||||
nameLabel.color = colorSelected;
|
||||
valueLabel.color = colorSelected;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDeselection implementation.
|
||||
/// </summary>
|
||||
public override void OnDeselection()
|
||||
{
|
||||
nameLabel.color = colorDefault;
|
||||
valueLabel.color = colorDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnIncrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">True if incrementing fast.</param>
|
||||
public override void OnIncrement(bool fast)
|
||||
{
|
||||
ChangeValue(fast, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnDecrement implementation.
|
||||
/// </summary>
|
||||
/// <param name="fast">Trye if decrementing fast.</param>
|
||||
public override void OnDecrement(bool fast)
|
||||
{
|
||||
ChangeValue(fast, -1);
|
||||
}
|
||||
|
||||
void ChangeValue(bool fast, int multiplier)
|
||||
{
|
||||
int value = m_Field.GetValue();
|
||||
value += m_Field.incStep * (fast ? m_Field.intStepMult : 1) * multiplier;
|
||||
m_Field.SetValue(value);
|
||||
UpdateValueLabel();
|
||||
}
|
||||
|
||||
void UpdateValueLabel()
|
||||
{
|
||||
if (valueLabel != null)
|
||||
valueLabel.text = m_Field.GetValue().ToString("N0");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue