260 lines
8.2 KiB
C#
260 lines
8.2 KiB
C#
//-----------------------------------------------------------------------
|
|
// <copyright file="ScaleManipulator.cs" company="Google">
|
|
//
|
|
// Copyright 2018 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// </copyright>
|
|
//-----------------------------------------------------------------------
|
|
|
|
// Modifications copyright © 2020 Unity Technologies ApS
|
|
|
|
using System;
|
|
|
|
#if !AR_FOUNDATION_PRESENT && !PACKAGE_DOCS_GENERATION
|
|
|
|
// Stub class definition used to fool version defines that this MonoScript exists (fixed in 19.3)
|
|
namespace UnityEngine.XR.Interaction.Toolkit.AR
|
|
{
|
|
/// <summary>
|
|
/// Controls the scale of an object via a Pinch gesture.
|
|
/// If an object is selected, then doing a pinch/zoom will modify the scale
|
|
/// of the object.
|
|
/// </summary>
|
|
[Obsolete("ARScaleInteractable has been replaced by the ARTransformer. Use the ARTransformer instead.")]
|
|
public class ARScaleInteractable { }
|
|
}
|
|
|
|
#else
|
|
|
|
using UnityEngine;
|
|
|
|
namespace UnityEngine.XR.Interaction.Toolkit.AR
|
|
{
|
|
/// <summary>
|
|
/// Controls the scale of an object via a Pinch gesture.
|
|
/// If an object is selected, then doing a pinch/zoom will modify the scale
|
|
/// of the object.
|
|
/// </summary>
|
|
[AddComponentMenu("XR/AR Scale Interactable", 22)]
|
|
[HelpURL(XRHelpURLConstants.k_ARScaleInteractable)]
|
|
[Obsolete("ARScaleInteractable has been replaced by the ARTransformer. Use the ARTransformer instead.")]
|
|
public class ARScaleInteractable : ARBaseGestureInteractable
|
|
{
|
|
[SerializeField, Tooltip("The minimum scale of the object.")]
|
|
float m_MinScale = 0.75f;
|
|
|
|
/// <summary>
|
|
/// The minimum scale of the object.
|
|
/// </summary>
|
|
public float minScale
|
|
{
|
|
get => m_MinScale;
|
|
set => m_MinScale = value;
|
|
}
|
|
|
|
[SerializeField, Tooltip("The maximum scale of the object.")]
|
|
float m_MaxScale = 1.75f;
|
|
|
|
/// <summary>
|
|
/// The maximum scale of the object.
|
|
/// </summary>
|
|
public float maxScale
|
|
{
|
|
get => m_MaxScale;
|
|
set => m_MaxScale = value;
|
|
}
|
|
|
|
[SerializeField, Tooltip("The elastic ratio used when scaling the object")]
|
|
float m_ElasticRatioLimit;
|
|
|
|
/// <summary>
|
|
/// The limit of the elastic ratio.
|
|
/// </summary>
|
|
public float elasticRatioLimit
|
|
{
|
|
get => m_ElasticRatioLimit;
|
|
set => m_ElasticRatioLimit = value;
|
|
}
|
|
|
|
[SerializeField, Tooltip("Sensitivity to movement being translated into scale.")]
|
|
float m_Sensitivity = 0.75f;
|
|
|
|
/// <summary>
|
|
/// Sensitivity to movement being translated into scale.
|
|
/// </summary>
|
|
public float sensitivity
|
|
{
|
|
get => m_Sensitivity;
|
|
set => m_Sensitivity = value;
|
|
}
|
|
|
|
[SerializeField, Tooltip("Amount that the scale bounces back after hitting min/max of range.")]
|
|
float m_Elasticity = 0.15f;
|
|
|
|
/// <summary>
|
|
/// Amount that the scale bounces back after hitting min/max of range.
|
|
/// </summary>
|
|
public float elasticity
|
|
{
|
|
get => m_Elasticity;
|
|
set => m_Elasticity = value;
|
|
}
|
|
|
|
float scaleDelta
|
|
{
|
|
get
|
|
{
|
|
if (m_MinScale > m_MaxScale)
|
|
{
|
|
Debug.LogError("minScale must be smaller than maxScale.", this);
|
|
return 0f;
|
|
}
|
|
|
|
return m_MaxScale - m_MinScale;
|
|
}
|
|
}
|
|
|
|
float clampedScaleRatio => Mathf.Clamp01(m_CurrentScaleRatio);
|
|
|
|
Vector3 currentScale
|
|
{
|
|
get
|
|
{
|
|
var elasticScaleRatio = clampedScaleRatio + ElasticDelta();
|
|
var elasticScale = m_MinScale + (elasticScaleRatio * scaleDelta);
|
|
return new Vector3(elasticScale, elasticScale, elasticScale);
|
|
}
|
|
}
|
|
|
|
float m_CurrentScaleRatio;
|
|
float m_CapturedMinScale;
|
|
float m_CapturedMaxScale;
|
|
bool m_IsScaling;
|
|
|
|
/// <summary>
|
|
/// See <see cref="MonoBehaviour"/>.
|
|
/// </summary>
|
|
protected void OnValidate()
|
|
{
|
|
m_MinScale = Mathf.Max(0f, m_MinScale);
|
|
m_MaxScale = Mathf.Max(Mathf.Max(0f, m_MinScale), m_MaxScale);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
UpdateCurrentScaleRatio();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
|
|
{
|
|
base.ProcessInteractable(updatePhase);
|
|
|
|
if (updatePhase == XRInteractionUpdateOrder.UpdatePhase.Late)
|
|
{
|
|
if (!m_IsScaling)
|
|
{
|
|
// Re-adjust ratio in case of min and max scale change.
|
|
if (!Mathf.Approximately(m_CapturedMinScale, m_MinScale) ||
|
|
!Mathf.Approximately(m_CapturedMaxScale, m_MaxScale))
|
|
{
|
|
UpdateCurrentScaleRatio();
|
|
}
|
|
|
|
m_CurrentScaleRatio = Mathf.Lerp(m_CurrentScaleRatio, clampedScaleRatio, Time.deltaTime * 8f);
|
|
transform.localScale = currentScale;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override bool CanStartManipulationForGesture(PinchGesture gesture)
|
|
{
|
|
return IsGameObjectSelected() && gesture.targetObject == null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recalculates the current scale ratio in case local scale, min or max scale were changed.
|
|
/// </summary>
|
|
/// <param name="gesture">The gesture that started this transformation.</param>
|
|
/// <inheritdoc />
|
|
protected override void OnStartManipulation(PinchGesture gesture)
|
|
{
|
|
m_IsScaling = true;
|
|
UpdateCurrentScaleRatio();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Continues the scaling of the object.
|
|
/// </summary>
|
|
/// <param name="gesture">The current gesture.</param>
|
|
/// <inheritdoc />
|
|
protected override void OnContinueManipulation(PinchGesture gesture)
|
|
{
|
|
m_CurrentScaleRatio += sensitivity * GestureTouchesUtility.PixelsToInches(gesture.gapDelta);
|
|
|
|
transform.localScale = currentScale;
|
|
|
|
// If we've tried to scale too far beyond the limit, then cancel the gesture
|
|
// to snap back within the scale range.
|
|
if (m_CurrentScaleRatio < -elasticRatioLimit
|
|
|| m_CurrentScaleRatio > (1f + elasticRatioLimit))
|
|
{
|
|
gesture.Cancel();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finishes the scaling of the object.
|
|
/// </summary>
|
|
/// <param name="gesture">The current gesture.</param>
|
|
/// <inheritdoc />
|
|
protected override void OnEndManipulation(PinchGesture gesture)
|
|
{
|
|
m_IsScaling = false;
|
|
}
|
|
|
|
void UpdateCurrentScaleRatio()
|
|
{
|
|
m_CurrentScaleRatio = scaleDelta != 0f ? (transform.localScale.x - m_MinScale) / scaleDelta : 1f;
|
|
m_CapturedMinScale = m_MinScale;
|
|
m_CapturedMaxScale = m_MaxScale;
|
|
}
|
|
|
|
float ElasticDelta()
|
|
{
|
|
float overRatio;
|
|
if (m_CurrentScaleRatio > 1f)
|
|
{
|
|
overRatio = m_CurrentScaleRatio - 1f;
|
|
}
|
|
else if (m_CurrentScaleRatio < 0f)
|
|
{
|
|
overRatio = m_CurrentScaleRatio;
|
|
}
|
|
else
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
return (1f - (1f / ((Mathf.Abs(overRatio) * elasticity) + 1f))) * Mathf.Sign(overRatio);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|