using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.LowLevel; using UnityEngine.InputSystem.Utilities; ////REVIEW: expose euler angle subcontrols? namespace UnityEngine.InputSystem.Controls { /// /// A generic input control reading quaternion (rotation) values. /// public class QuaternionControl : InputControl { // Accessing these components as individual controls usually doesn't make too much sense, // but having these controls allows changing the state format on the quaternion without // requiring the control to explicitly support the various different storage formats. // Also, it allows putting processors on the individual components which may be necessary // to properly convert the source data. /// /// The X component of the quaternion. /// /// Control representing the X component. [InputControl(displayName = "X")] public AxisControl x { get; set; } /// /// The Y component of the quaternion. /// /// Control representing the Y component. [InputControl(displayName = "Y")] public AxisControl y { get; set; } /// /// The Z component of the quaternion. /// /// Control representing the Z component. [InputControl(displayName = "Z")] public AxisControl z { get; set; } /// /// The W component of the quaternion. /// /// Control representing the W component. [InputControl(displayName = "W")] public AxisControl w { get; set; } /// /// Default-initialize the control. /// public QuaternionControl() { m_StateBlock.sizeInBits = sizeof(float) * 4 * 8; m_StateBlock.format = InputStateBlock.FormatQuaternion; } /// protected override void FinishSetup() { x = GetChildControl("x"); y = GetChildControl("y"); z = GetChildControl("z"); w = GetChildControl("w"); base.FinishSetup(); } /// public override unsafe Quaternion ReadUnprocessedValueFromState(void* statePtr) { switch (m_OptimizedControlDataType) { case InputStateBlock.kFormatQuaternion: return *(Quaternion*)((byte*)statePtr + (int)m_StateBlock.byteOffset); default: return new Quaternion( x.ReadValueFromStateWithCaching(statePtr), y.ReadValueFromStateWithCaching(statePtr), z.ReadValueFromStateWithCaching(statePtr), w.ReadUnprocessedValueFromStateWithCaching(statePtr)); } } /// public override unsafe void WriteValueIntoState(Quaternion value, void* statePtr) { switch (m_OptimizedControlDataType) { case InputStateBlock.kFormatQuaternion: *(Quaternion*)((byte*)statePtr + (int)m_StateBlock.byteOffset) = value; break; default: x.WriteValueIntoState(value.x, statePtr); y.WriteValueIntoState(value.y, statePtr); z.WriteValueIntoState(value.z, statePtr); w.WriteValueIntoState(value.w, statePtr); break; } } protected override FourCC CalculateOptimizedControlDataType() { if ( m_StateBlock.sizeInBits == sizeof(float) * 4 * 8 && m_StateBlock.bitOffset == 0 && x.optimizedControlDataType == InputStateBlock.FormatFloat && y.optimizedControlDataType == InputStateBlock.FormatFloat && z.optimizedControlDataType == InputStateBlock.FormatFloat && w.optimizedControlDataType == InputStateBlock.FormatFloat && y.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 4 && z.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 8 && w.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 12 && // For some unknown reason ReadUnprocessedValueFromState here uses ReadValueFromState on x/y/z (but not w) // which means we can't optimize if there any processors on x/y/z x.m_ProcessorStack.length == 0 && y.m_ProcessorStack.length == 0 && z.m_ProcessorStack.length == 0 ) return InputStateBlock.FormatQuaternion; return InputStateBlock.FormatInvalid; } } }