class __declspec()

in Kits/ATGTK/OrbitCamera.cpp [120:544]


class __declspec(align(16)) OrbitCamera::Impl
{
public:
	Impl() :
		m_nearDistance(0.1f),
		m_farDistance(10000.f),
		m_fov(XM_PI / 4.f),
		m_defaultSensitivity(1.f),
		m_minSensitivity(0.01f),
		m_maxSensitivity(10.f),
		m_stepSensitivity(0.01f),
		m_defaultRadius(5.f),
		m_minRadius(1.f),
		m_maxRadius(FLT_MAX),
		m_rotRate(1.f),
		m_radiusRate(1.f),
		m_flags(0),
		m_lhcoords(false),
#ifdef _XBOX_ONE
		m_width(1920),
		m_height(1080)
#else
		m_width(1280),
		m_height(720)
#endif
	{
		m_view = m_projection = XMMatrixIdentity();
		m_cameraPosition = m_homeFocus = g_XMZero;
		m_homeRotation = g_XMIdentityR3;

		m_bounds.Center.x = m_bounds.Center.y = m_bounds.Center.z = 0.f;
		m_bounds.Extents.x = m_bounds.Extents.y = m_bounds.Extents.z = FLT_MAX;

		Reset();
	}

	void Update(float elapsedTime, const GamePad::State& pad)
	{
		using namespace DirectX::SimpleMath;

		float handed = (m_lhcoords) ? 1.f : -1.f;

		Matrix im = XMMatrixInverse(nullptr, GetView());

		if (!(m_flags & c_FlagsDisableTranslation))
		{
			// Translate camera
			Vector3 move = Vector3::Zero;

			if (pad.IsDPadUpPressed())
			{
				move.y += 1.f;
			}
			else if (pad.IsDPadDownPressed())
			{
				move.y -= 1.f;
			}

			if (pad.IsDPadLeftPressed())
			{
				move.x -= 1.f;
			}
			else if (pad.IsDPadRightPressed())
			{
				move.x += 1.f;
			}

			if (move.x != 0 || move.y != 0)
			{
				move = Vector3::TransformNormal(move, im);

				m_focus = XMVectorAdd(m_focus, XMVectorScale(move, elapsedTime * m_sensitivity));

				Vector3 minBound = m_bounds.Center - m_bounds.Extents;
				Vector3 maxBound = m_bounds.Center + m_bounds.Extents;
				m_focus = XMVectorMax(minBound, XMVectorMin(maxBound, m_focus));

				m_viewDirty = true;
			}
		}

		// Rotate camera
		Vector3 orbit(pad.thumbSticks.rightX, pad.thumbSticks.rightY, pad.thumbSticks.leftX);
		orbit *= elapsedTime * m_rotRate;

		if (orbit.x != 0 || orbit.y != 0 || orbit.z != 0)
		{
			m_cameraRotation = XMQuaternionMultiply(m_cameraRotation, XMQuaternionRotationAxis(im.Right(), orbit.y * handed));
			m_cameraRotation = XMQuaternionMultiply(m_cameraRotation, XMQuaternionRotationAxis(im.Up(), -orbit.x * handed));

			if (!(m_flags & c_FlagsDisableRollZ))
			{
				m_cameraRotation = XMQuaternionMultiply(m_cameraRotation, XMQuaternionRotationAxis(im.Forward(), orbit.z));
			}

			m_cameraRotation = XMQuaternionNormalize(m_cameraRotation);
			m_viewDirty = true;
		}

		if (!(m_flags & c_FlagsDisableRadiusControl))
		{
			if (pad.thumbSticks.leftY != 0)
			{
				m_radius -= pad.thumbSticks.leftY * elapsedTime * m_radiusRate;
				m_radius = std::max(m_minRadius, std::min(m_maxRadius, m_radius));
				m_viewDirty = true;
			}
		}

		// Other controls
		if (pad.IsLeftShoulderPressed() && pad.IsRightShoulderPressed())
		{
			m_sensitivity = m_defaultSensitivity;

#ifdef _DEBUG
			(void)GetView();

			char buff[128] = {};
			Vector4 tmp = m_cameraPosition;
			sprintf_s(buff, "cameraPosition = { %2.2ff, %2.2ff, %2.2ff, 0.f};\n", tmp.x, tmp.y, tmp.z);
			OutputDebugStringA(buff);

			tmp = m_cameraRotation;
			sprintf_s(buff, "cameraRotation = { %2.2ff, %2.2ff, %2.2ff, %2.2ff};\n", tmp.x, tmp.y, tmp.z, tmp.w);
			OutputDebugStringA(buff);
#endif
		}
		else if (!(m_flags & c_FlagsDisableSensitivityControl))
		{
			if (pad.IsRightShoulderPressed())
			{
				m_sensitivity += m_stepSensitivity;
				if (m_sensitivity > m_maxSensitivity)
					m_sensitivity = m_maxSensitivity;
			}
			else if (pad.IsLeftShoulderPressed())
			{
				m_sensitivity -= m_stepSensitivity;
				if (m_sensitivity < m_minSensitivity)
					m_sensitivity = m_minSensitivity;
			}
		}

		if (pad.IsRightStickPressed())
		{
			Reset();
		}

		if (pad.IsLeftStickPressed() && !(m_flags & c_FlagsDisableFrameExtentsReset))
		{
			m_radius = m_defaultRadius;
			m_focus = m_homeFocus;
			m_viewDirty = true;
		}
	}

	void Update(float elapsedTime, Mouse& mouse, Keyboard& kb)
	{
		using namespace DirectX::SimpleMath;

		float handed = (m_lhcoords) ? 1.f : -1.f;

		Matrix im = XMMatrixInverse(nullptr, GetView());

		auto mstate = mouse.GetState();
		auto kbstate = kb.GetState();

		if ((mstate.positionMode != Mouse::MODE_RELATIVE) && !m_arcBall.IsDragging())
		{
			// Keyboard controls
			if (m_flags & c_FlagsArrowKeysOrbit)
			{
				Vector3 orbit = Vector3::Zero;

				if (kbstate.Up || kbstate.W)
					orbit.y = 1.f;

				if (kbstate.Down || kbstate.S)
					orbit.y = -1.f;

				if (kbstate.Right || kbstate.D)
					orbit.x = 1.f;

				if (kbstate.Left || kbstate.A)
					orbit.x = -1.f;

				if (kbstate.Q)
					orbit.z = -1.f;

				if (kbstate.E)
					orbit.z = 1.f;

				if (orbit.x != 0 || orbit.y != 0 || orbit.z != 0)
				{
					orbit *= elapsedTime * m_rotRate;

					m_cameraRotation = XMQuaternionMultiply(m_cameraRotation, XMQuaternionRotationAxis(im.Right(), orbit.y * handed));
					m_cameraRotation = XMQuaternionMultiply(m_cameraRotation, XMQuaternionRotationAxis(im.Up(), -orbit.x * handed));

					if (!(m_flags & c_FlagsDisableRollZ))
					{
						m_cameraRotation = XMQuaternionMultiply(m_cameraRotation, XMQuaternionRotationAxis(im.Forward(), orbit.z));
					}

					m_cameraRotation = XMQuaternionNormalize(m_cameraRotation);
					m_viewDirty = true;
				}
			}
			else if (!(m_flags & c_FlagsDisableTranslation))
			{
				// Arrow keys & WASD control translation of camera focus
				Vector3 move = Vector3::Zero;

				float scale = m_radius;
				if (kbstate.LeftShift || kbstate.RightShift)
					scale *= 0.5f;

				if (m_flags & c_FlagsArrowKeys_XZ)
				{
					if (kbstate.PageUp)
						move.y += scale;

					if (kbstate.PageDown)
						move.y -= scale;

					if (kbstate.Up || kbstate.W)
						move.z += scale * handed;

					if (kbstate.Down || kbstate.S)
						move.z -= scale * handed;
				}
				else
				{
					if (kbstate.Up || kbstate.W)
						move.y += scale;

					if (kbstate.Down || kbstate.S)
						move.y -= scale;

					if (kbstate.PageUp)
						move.z += scale * handed;

					if (kbstate.PageDown)
						move.z -= scale * handed;
				}

				if (kbstate.Right || kbstate.D)
					move.x += scale;

				if (kbstate.Left || kbstate.A)
					move.x -= scale;

				if (move.x != 0 || move.y != 0 || move.z != 0)
				{
					move = Vector3::TransformNormal(move, im);

					m_focus = XMVectorAdd(m_focus, XMVectorScale(move, elapsedTime));

					Vector3 minBound = m_bounds.Center - m_bounds.Extents;
					Vector3 maxBound = m_bounds.Center + m_bounds.Extents;
					m_focus = XMVectorMax(minBound, XMVectorMin(maxBound, m_focus));

					m_viewDirty = true;
				}
			}

			if (kbstate.Home)
			{
				Reset();
			}
			else if (kbstate.End && !(m_flags & c_FlagsDisableFrameExtentsReset))
			{
				m_radius = m_defaultRadius;
				m_focus = m_homeFocus;
				m_viewDirty = true;
			}
		}

		// Mouse controls
		if (mstate.positionMode == Mouse::MODE_RELATIVE)
		{
			if (!(m_flags & c_FlagsDisableTranslation))
			{
				// Translate camera
				Vector3 delta;
				if (kbstate.LeftShift || kbstate.RightShift)
				{
					delta = Vector3(0.f, 0.f, -float(mstate.y) * handed) * m_radius * elapsedTime;
				}
				else
				{
					delta = Vector3(-float(mstate.x), float(mstate.y), 0.f) * m_radius * elapsedTime;
				}

				delta = Vector3::TransformNormal(delta, im);

				m_focus = XMVectorAdd(m_focus, XMVectorScale(delta, elapsedTime * m_sensitivity));

				Vector3 minBound = m_bounds.Center - m_bounds.Extents;
				Vector3 maxBound = m_bounds.Center + m_bounds.Extents;
				m_focus = XMVectorMax(minBound, XMVectorMin(maxBound, m_focus));

				m_viewDirty = true;
			}
		}
		else if (m_arcBall.IsDragging())
		{
			// Rotate camera
			m_arcBall.OnMove(mstate.x, mstate.y);
			m_cameraRotation = XMQuaternionInverse(m_arcBall.GetQuat());
			m_viewDirty = true;
		}
		else if (!(m_flags & c_FlagsDisableRadiusControl))
		{
			// Radius with scroll wheel
			m_radius = m_defaultRadius - ((float(mstate.scrollWheelValue) / 120.f) * m_radiusRate);
			m_radius = std::max(m_minRadius, std::min(m_maxRadius, m_radius));
			m_viewDirty = true;
		}

		if (!m_arcBall.IsDragging())
		{
			if (mstate.rightButton && mstate.positionMode == Mouse::MODE_ABSOLUTE)
				mouse.SetMode(Mouse::MODE_RELATIVE);
			else if (!mstate.rightButton && mstate.positionMode == Mouse::MODE_RELATIVE)
				mouse.SetMode(Mouse::MODE_ABSOLUTE);

			if (mstate.leftButton)
			{
				m_arcBall.OnBegin(mstate.x, mstate.y, XMQuaternionInverse(m_cameraRotation));
			}
		}
		else if (!mstate.leftButton)
		{
			m_arcBall.OnEnd();
		}
	}

	void Reset()
	{
		m_focus = m_homeFocus;
		m_radius = m_defaultRadius;
		m_cameraRotation = m_homeRotation;
		m_sensitivity = m_defaultSensitivity;
		m_viewDirty = m_projDirty = true;
		m_arcBall.Reset();
		m_arcBall.OnEnd();
	}

	mutable XMMATRIX        m_view;
	mutable XMMATRIX        m_projection;
	mutable XMVECTOR        m_cameraPosition;

	XMVECTOR                m_focus;
	XMVECTOR                m_homeFocus;

	XMVECTOR                m_cameraRotation;
	XMVECTOR                m_homeRotation;

	float                   m_nearDistance;
	float                   m_farDistance;
	float                   m_fov;
	float                   m_sensitivity;
	float                   m_defaultSensitivity;
	float                   m_minSensitivity;
	float                   m_maxSensitivity;
	float                   m_stepSensitivity;
	float                   m_radius;
	float                   m_defaultRadius;
	float                   m_minRadius;
	float                   m_maxRadius;
	float                   m_rotRate;
	float                   m_radiusRate;
	unsigned int            m_flags;

	bool                    m_lhcoords;
	mutable bool            m_viewDirty;
	mutable bool            m_projDirty;

	int                     m_width;
	int                     m_height;

	DirectX::BoundingBox    m_bounds;

	ArcBall                 m_arcBall;

	XMMATRIX GetView() const
	{
		m_viewDirty = false;

		XMVECTOR dir = XMVector3Rotate((m_lhcoords) ? g_XMNegIdentityR2 : g_XMIdentityR2, m_cameraRotation);
		XMVECTOR up = XMVector3Rotate(g_XMIdentityR1, m_cameraRotation);

		m_cameraPosition = XMVectorAdd(m_focus, XMVectorScale(dir, m_radius));

		if (m_lhcoords)
		{
			m_view = XMMatrixLookAtLH(m_cameraPosition, m_focus, up);
		}
		else
		{
			m_view = XMMatrixLookAtRH(m_cameraPosition, m_focus, up);
		}

		return m_view;
	}

	XMMATRIX GetProjection() const
	{
		m_projDirty = false;

		float aspectRatio = (m_height > 0.f) ? (float(m_width) / float(m_height)) : 1.f;

		if (m_lhcoords)
		{
			m_projection = XMMatrixPerspectiveFovLH(m_fov, aspectRatio, m_nearDistance, m_farDistance);
		}
		else
		{
			m_projection = XMMatrixPerspectiveFovRH(m_fov, aspectRatio, m_nearDistance, m_farDistance);
		}

		return m_projection;
	}
};