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;
}
}
}