Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce ScaleMaintainType #976

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the BSD 3-Clause

using System;
using UnityEngine;

namespace MixedReality.Toolkit.SpatialManipulation
{
Expand Down Expand Up @@ -56,12 +57,12 @@ public enum HandleType
/// A handle that is mounted to the edge of a <see cref="BoundsControl"/>, and can rotate the object.
/// </summary>
Rotation = 1 << 0,

/// <summary>
/// A handle that is mounted to the corner of a <see cref="BoundsControl"/>, and can scale the object.
/// </summary>
Scale = 1 << 1,

/// <summary>
/// A handle that is mounted to the face of a <see cref="BoundsControl"/>, and can move the object.
/// </summary>
Expand Down Expand Up @@ -109,4 +110,28 @@ public enum FlattenMode
/// </summary>
Always,
}

/// <summary>
/// Scale adjusting type that is used for determining how to maintain target scale of bounds control.
/// </summary>
public enum ScaleMaintainType
{
/// <summary>
/// Maintain global size, even as the object changes size.
/// </summary>
[Tooltip("Maintain global size, even as the object changes size.")]
GlobalSize,

/// <summary>
/// Adjust the handle's scale based on the initial parent scale.
/// </summary>
[Tooltip("Adjust the handle's scale based on the initial parent scale.")]
FixedScale,

/// <summary>
/// Adjust the handle's scale to be the same size regardless of the initial parent scale and clamp the scale to a min and max value.
/// </summary>
[Tooltip("Adjust the handle's scale to be the same size regardless of the initial parent scale and clamp the scale to a min and max value.")]
Advanced
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,34 @@ public BoundsControl BoundsControlRoot
}

[SerializeField]
[Tooltip("Should the handle maintain its global size, even as the object changes size?")]
private bool maintainGlobalSize = true;
[Tooltip("How should the handle scale be maintained?")]
private ScaleMaintainType scaleMaintainType = ScaleMaintainType.GlobalSize;

// For backward compatibilty, the scaleAdjustType is set according to the maintainGlobalSize property on start.
private bool maintainGlobalSize = true;
/// <summary>
/// Should the handle maintain its global size, even as the object changes size?
/// </summary>
public bool MaintainGlobalSize { get => maintainGlobalSize; set => maintainGlobalSize = value;}
[Obsolete("Use ScaleMaintainType instead.")]
public bool MaintainGlobalSize
{
get => scaleMaintainType == ScaleMaintainType.GlobalSize;
set => scaleMaintainType = value ? ScaleMaintainType.GlobalSize : ScaleMaintainType.FixedScale;
}

private float targetParentScale = 1f;

[SerializeField]
[Tooltip("Target lossy scale for the handle. Set value only applicable if ScaleAdjustType is Advanced.")]
private float targetLossyScale = 2f;

[SerializeField]
[Tooltip("Minimum lossy scale for the handle. Only applicable if ScaleAdjustType is Advanced.")]
private float minLossyScale = 1f;

[SerializeField]
[Tooltip("Maximum lossy scale for the handle. Only applicable if ScaleAdjustType is Advanced.")]
private float maxLossyScale = 4f;

#region ISnapInteractable

Expand Down Expand Up @@ -89,10 +110,6 @@ public BoundsControl BoundsControlRoot

private bool wasOccludedLastFrame = false;

private float initialParentScale;

private float initialLocalScale;

/// <inheritdoc/>
protected override void Awake()
{
Expand All @@ -102,17 +119,21 @@ protected override void Awake()
DisableInteractorType(typeof(IPokeInteractor));

handleRenderer = GetComponentInChildren<MeshRenderer>();
scaleMaintainType = maintainGlobalSize ? ScaleMaintainType.GlobalSize : ScaleMaintainType.FixedScale;
}

/// <summary>
/// A Unity event function that is called on the frame when a script is enabled just before any of the update methods are called the first time.
/// </summary>
public void Start()
{
// Record initial values at Start(), so that we
// capture the bounds sizing, etc.
initialParentScale = MaxComponent(transform.parent.lossyScale);
initialLocalScale = MaxComponent(transform.localScale);
if (scaleMaintainType != ScaleMaintainType.Advanced)
{
// Record initial values at Start(), so that we
// capture the bounds sizing, etc.
targetParentScale = MaxComponent(transform.parent.lossyScale);
targetLossyScale = MaxComponent(transform.localScale);
}
}

/// <summary>
Expand All @@ -132,26 +153,69 @@ protected virtual void LateUpdate()
}

// Maintain the aspect ratio/proportion of the handles, globally.
// Setting localScale to ensure that lossyScale remains equal to initialLocalScale across all axes.
UpdateLocalScale();
}

protected virtual void UpdateLocalScale()
{
transform.localScale = Vector3.one;
transform.localScale = new Vector3(
transform.lossyScale.x == 0 ? transform.localScale.x : (initialLocalScale / transform.lossyScale.x),
transform.lossyScale.y == 0 ? transform.localScale.y : (initialLocalScale / transform.lossyScale.y),
transform.lossyScale.z == 0 ? transform.localScale.z : (initialLocalScale / transform.lossyScale.z));

// If we don't want to maintain the overall *size*, we scale
// by the maximum component of the box so that the handles grow/shrink
// with the overall box manipulation.
if (!maintainGlobalSize && initialParentScale != 0)

switch (scaleMaintainType)
{
transform.localScale = transform.localScale * (MaxComponent(transform.parent.lossyScale) / initialParentScale);
case ScaleMaintainType.GlobalSize:
transform.localScale = GetLocalScale(targetLossyScale);
break;

case ScaleMaintainType.FixedScale:
transform.localScale = GetLocalScale(targetLossyScale);

// If we don't want to maintain the overall *size*, we scale
// by the maximum component of the box so that the handles grow/shrink
// with the overall box manipulation.
if (targetParentScale != 0)
{
transform.localScale = transform.localScale * (MaxComponent(transform.parent.lossyScale) / targetParentScale);
}
break;

case ScaleMaintainType.Advanced:
// Find the local scale that would result in the target lossy
// scale (the desired scale if the parent scale is 1)
Vector3 targetScale = GetLocalScale(targetLossyScale);
Vector3 minScale = GetLocalScale(minLossyScale);
Vector3 maxScale = GetLocalScale(maxLossyScale);

// We scale by the maximum component of the box so that
// the handles grow/shrink with the overall box manipulation.
transform.localScale = targetScale * (MaxComponent(transform.parent.lossyScale) / targetParentScale);

// If this scale is greater than our desired lossy scale then clamp it to the max lossy scale
if (MaxComponent(transform.lossyScale) > maxLossyScale)
{
transform.localScale = maxScale;
}
// If this scale is less than our desired lossy scale then clamp it to the min lossy scale
else if (MinComponent(transform.lossyScale) < minLossyScale)
{
transform.localScale = minScale;
}
break;

default:
break;
}
}

private float MaxComponent(Vector3 v)
{
return Mathf.Max(Mathf.Max(Mathf.Abs(v.x), Mathf.Abs(v.y)), Mathf.Abs(v.z));
}
// Returns the local scale this transform needs to have in order to have the desired lossy scale
protected Vector3 GetLocalScale(float lossyScale) => new(
transform.lossyScale.x == 0 ? transform.localScale.x : (lossyScale / transform.lossyScale.x),
transform.lossyScale.y == 0 ? transform.localScale.y : (lossyScale / transform.lossyScale.y),
transform.lossyScale.z == 0 ? transform.localScale.z : (lossyScale / transform.lossyScale.z)
);

protected float MaxComponent(Vector3 v) => Mathf.Max(Mathf.Max(Mathf.Abs(v.x), Mathf.Abs(v.y)), Mathf.Abs(v.z));

protected float MinComponent(Vector3 v) => Mathf.Min(Mathf.Max(Mathf.Abs(v.x), Mathf.Abs(v.y)), Mathf.Abs(v.z));

/// <summary>
/// Occludes the handle so it is not initially visible when it should start disabled.
Expand All @@ -163,7 +227,7 @@ public void HideOnStartup()
{
handleRenderer.enabled = false;
}
if (colliders.Count > 0 && colliders[0] != null)
if (colliders.Count > 0 && colliders[0] != null)
{
colliders[0].enabled = false;
}
Expand All @@ -183,7 +247,7 @@ internal void ForceOcclusion()
{
handleRenderer.enabled = false;
}
if (colliders.Count > 0 && colliders[0] != null)
if (colliders.Count > 0 && colliders[0] != null)
{
colliders[0].enabled = false;
}
Expand Down
4 changes: 4 additions & 0 deletions org.mixedrealitytoolkit.spatialmanipulation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## Unreleased

### Added

* Added different types of maintaining scale for bounds handles. [PR #976](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/976)

### Fixed

* Fixed tap to place `StartPlacement()` when called just after instantiation of the component. [PR #785](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/785)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Mixed Reality Toolkit Contributors
// Licensed under the BSD 3-Clause

using MixedReality.Toolkit.Editor;
using UnityEditor;

namespace MixedReality.Toolkit.SpatialManipulation.Editor
{
[CustomEditor(typeof(BoundsHandleInteractable), true)]
[CanEditMultipleObjects]
public class BoundsHandleInteractableEditor : StatefulInteractableEditor
{
private SerializedProperty scaleMaintainType;
private SerializedProperty targetLossyScale;
private SerializedProperty minLossyScale;
private SerializedProperty maxLossyScale;
private SerializedProperty handleType;

protected override void OnEnable()
{
base.OnEnable();

scaleMaintainType = serializedObject.FindProperty("scaleMaintainType");
targetLossyScale = serializedObject.FindProperty("targetLossyScale");
minLossyScale = serializedObject.FindProperty("minLossyScale");
maxLossyScale = serializedObject.FindProperty("maxLossyScale");
handleType = serializedObject.FindProperty("handleType");
}

public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();

EditorGUILayout.PropertyField(scaleMaintainType);

if (scaleMaintainType.enumValueIndex == (int)ScaleMaintainType.Advanced)
{
EditorGUILayout.PropertyField(targetLossyScale);
EditorGUILayout.PropertyField(minLossyScale);
EditorGUILayout.PropertyField(maxLossyScale);
}

EditorGUILayout.PropertyField(handleType);

serializedObject.ApplyModifiedProperties();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion org.mixedrealitytoolkit.spatialmanipulation/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "org.mixedrealitytoolkit.spatialmanipulation",
"version": "3.3.1-development",
"version": "3.3.2-development",
"description": "Spatial manipulation features, including ObjectManipulator, BoundsControl, and the Solvers/Constraints systems.",
"displayName": "MRTK Spatial Manipulation",
"msftFeatureCategory": "MRTK3",
Expand Down