313 lines
16 KiB
C#
313 lines
16 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using Microsoft.MixedReality.Toolkit.UI;
|
|
using Microsoft.MixedReality.Toolkit.UI.BoundsControl;
|
|
using Microsoft.MixedReality.Toolkit.UI.BoundsControlTypes;
|
|
using System.Collections.Generic;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.Utilities
|
|
{
|
|
/// <summary>
|
|
/// Migration handler for migrating bounding box gameobjects to bounds control gameobjects.
|
|
/// </summary>
|
|
public class BoundsControlMigrationHandler : IMigrationHandler
|
|
{
|
|
/// <inheritdoc />
|
|
public bool CanMigrate(GameObject gameObject)
|
|
{
|
|
return gameObject.GetComponent<BoundingBox>() != null;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Migrate(GameObject gameObject)
|
|
{
|
|
var boundingBox = gameObject.GetComponent<BoundingBox>();
|
|
var boundsControl = gameObject.EnsureComponent<BoundsControl>();
|
|
|
|
boundsControl.enabled = boundingBox.enabled;
|
|
|
|
{
|
|
Undo.RecordObject(gameObject, "BoundsControl migration: swapping BoundingBox with BoundsControl.");
|
|
|
|
// migrate logic settings
|
|
boundsControl.Target = boundingBox.Target;
|
|
boundsControl.BoundsOverride = boundingBox.BoundsOverride;
|
|
boundsControl.CalculationMethod = MigrateCalculationMethod(boundingBox.CalculationMethod);
|
|
boundsControl.BoundsControlActivation = MigrateActivationFlag(boundingBox.BoundingBoxActivation);
|
|
|
|
// only carry over min max scaling values if user hasn't attached min max scale constraint component yet
|
|
if (gameObject.GetComponent<MinMaxScaleConstraint>() == null)
|
|
{
|
|
#pragma warning disable 0618
|
|
// create a minmaxscaleconstraint in case there's a min max scale set up
|
|
if (boundingBox.ScaleMinimum != 0.0f || boundingBox.ScaleMaximum != 0.0f)
|
|
{
|
|
MinMaxScaleConstraint scaleConstraint = gameObject.AddComponent<MinMaxScaleConstraint>();
|
|
scaleConstraint.ScaleMinimum = boundingBox.ScaleMinimum;
|
|
scaleConstraint.ScaleMaximum = boundingBox.ScaleMaximum;
|
|
}
|
|
#pragma warning restore 0618
|
|
}
|
|
|
|
// migrate visuals
|
|
boundsControl.FlattenAxis = MigrateFlattenAxis(boundingBox.FlattenAxis);
|
|
boundsControl.BoxPadding = boundingBox.BoxPadding;
|
|
MigrateBoxDisplay(boundsControl, boundingBox);
|
|
MigrateLinks(boundsControl, boundingBox);
|
|
MigrateScaleHandles(boundsControl, boundingBox);
|
|
MigrateRotationHandles(boundsControl, boundingBox);
|
|
MigrateProximityEffect(boundsControl, boundingBox);
|
|
|
|
// debug properties
|
|
boundsControl.DebugText = boundingBox.debugText;
|
|
boundsControl.HideElementsInInspector = boundingBox.HideElementsInInspector;
|
|
|
|
// events
|
|
boundsControl.RotateStarted = boundingBox.RotateStarted;
|
|
boundsControl.RotateStopped = boundingBox.RotateStopped;
|
|
boundsControl.ScaleStarted = boundingBox.ScaleStarted;
|
|
boundsControl.ScaleStopped = boundingBox.ScaleStopped;
|
|
}
|
|
|
|
// look in the scene for app bars and upgrade them too to point to the new component
|
|
MigrateAppBar(boundingBox, boundsControl);
|
|
|
|
{
|
|
Undo.RecordObject(gameObject, "Removing obsolete BoundingBox component");
|
|
// destroy obsolete component
|
|
Object.DestroyImmediate(boundingBox);
|
|
}
|
|
}
|
|
|
|
#region Flags Migration
|
|
|
|
private BoundsCalculationMethod MigrateCalculationMethod(BoundingBox.BoundsCalculationMethod calculationMethod)
|
|
{
|
|
switch (calculationMethod)
|
|
{
|
|
case BoundingBox.BoundsCalculationMethod.RendererOverCollider:
|
|
return BoundsCalculationMethod.RendererOverCollider;
|
|
case BoundingBox.BoundsCalculationMethod.ColliderOverRenderer:
|
|
return BoundsCalculationMethod.ColliderOverRenderer;
|
|
case BoundingBox.BoundsCalculationMethod.ColliderOnly:
|
|
return BoundsCalculationMethod.ColliderOnly;
|
|
case BoundingBox.BoundsCalculationMethod.RendererOnly:
|
|
return BoundsCalculationMethod.RendererOnly;
|
|
}
|
|
|
|
Debug.Assert(false, "Tried to migrate unsupported bounds calculation method in bounding box / bounds control");
|
|
return BoundsCalculationMethod.RendererOverCollider;
|
|
}
|
|
|
|
private BoundsControlActivationType MigrateActivationFlag(BoundingBox.BoundingBoxActivationType activationFlag)
|
|
{
|
|
switch (activationFlag)
|
|
{
|
|
case BoundingBox.BoundingBoxActivationType.ActivateOnStart:
|
|
return BoundsControlActivationType.ActivateOnStart;
|
|
case BoundingBox.BoundingBoxActivationType.ActivateByProximity:
|
|
return BoundsControlActivationType.ActivateByProximity;
|
|
case BoundingBox.BoundingBoxActivationType.ActivateByPointer:
|
|
return BoundsControlActivationType.ActivateByPointer;
|
|
case BoundingBox.BoundingBoxActivationType.ActivateByProximityAndPointer:
|
|
return BoundsControlActivationType.ActivateByProximityAndPointer;
|
|
case BoundingBox.BoundingBoxActivationType.ActivateManually:
|
|
return BoundsControlActivationType.ActivateManually;
|
|
}
|
|
|
|
Debug.Assert(false, "Tried to migrate unsupported activation flag in bounding box / bounds control");
|
|
return BoundsControlActivationType.ActivateOnStart;
|
|
}
|
|
|
|
private FlattenModeType MigrateFlattenAxis(BoundingBox.FlattenModeType flattenAxisType)
|
|
{
|
|
switch (flattenAxisType)
|
|
{
|
|
case BoundingBox.FlattenModeType.DoNotFlatten:
|
|
return FlattenModeType.DoNotFlatten;
|
|
case BoundingBox.FlattenModeType.FlattenX:
|
|
return FlattenModeType.FlattenX;
|
|
case BoundingBox.FlattenModeType.FlattenY:
|
|
return FlattenModeType.FlattenY;
|
|
case BoundingBox.FlattenModeType.FlattenZ:
|
|
return FlattenModeType.FlattenZ;
|
|
case BoundingBox.FlattenModeType.FlattenAuto:
|
|
return FlattenModeType.FlattenAuto;
|
|
}
|
|
|
|
Debug.Assert(false, "Tried to migrate unsupported flatten axis type in bounding box / bounds control");
|
|
return FlattenModeType.DoNotFlatten;
|
|
}
|
|
|
|
private WireframeType MigrateWireframeShape(BoundingBox.WireframeType wireframeType)
|
|
{
|
|
switch (wireframeType)
|
|
{
|
|
case BoundingBox.WireframeType.Cubic:
|
|
return WireframeType.Cubic;
|
|
case BoundingBox.WireframeType.Cylindrical:
|
|
return WireframeType.Cylindrical;
|
|
}
|
|
|
|
Debug.Assert(false, "Tried to migrate unsupported wireframe type in bounding box / bounds control");
|
|
return WireframeType.Cubic;
|
|
}
|
|
|
|
private HandlePrefabCollider MigrateRotationHandleColliderType(BoundingBox.RotationHandlePrefabCollider rotationHandlePrefabColliderType)
|
|
{
|
|
switch (rotationHandlePrefabColliderType)
|
|
{
|
|
case BoundingBox.RotationHandlePrefabCollider.Sphere:
|
|
return HandlePrefabCollider.Sphere;
|
|
case BoundingBox.RotationHandlePrefabCollider.Box:
|
|
return HandlePrefabCollider.Box;
|
|
}
|
|
|
|
Debug.Assert(false, "Tried to migrate unsupported rotation handle collider type in bounding box / bounds control");
|
|
return HandlePrefabCollider.Sphere;
|
|
}
|
|
|
|
#endregion Flags Migration
|
|
|
|
#region Visuals Configuration Migration
|
|
|
|
private T EnsureConfiguration<T>(GameObject owner, T config, bool enforceScriptableCreation) where T : ScriptableObject
|
|
{
|
|
var instance = config;
|
|
if (instance == null || enforceScriptableCreation)
|
|
{
|
|
instance = ScriptableObject.CreateInstance<T>();
|
|
// scriptables in prefabs need to be added into their asset file as serializedobject, else they won't get stored
|
|
string objectPath = owner.scene.path;
|
|
if (objectPath.EndsWith(".prefab"))
|
|
{
|
|
instance.name = instance.GetType().Name;
|
|
AssetDatabase.AddObjectToAsset(instance, objectPath);
|
|
}
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method checks if the given object has a property override for any of the given property names.
|
|
/// It's used to figure out if scriptable objects have to be created for the current prefab / gameobject instance level.
|
|
/// </summary>
|
|
/// <param name="target">The target object (can be prefab component or component instance).</param>
|
|
/// <param name="propertyNames">Property names to be checked.</param>
|
|
/// <returns>Returns true if any of the passed property names have an override.</returns>
|
|
bool HasPropertyOverrides(Object target, List<string> propertyNames)
|
|
{
|
|
var propertyModifications = PrefabUtility.GetPropertyModifications(target);
|
|
if (propertyModifications != null)
|
|
{
|
|
foreach (var propertyMod in propertyModifications)
|
|
{
|
|
if (propertyNames.Contains(propertyMod.propertyPath))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void MigrateBoxDisplay(BoundsControl control, BoundingBox box)
|
|
{
|
|
List<string> propertyPaths = new List<string> { "boxMaterial", "boxGrabbedMaterial", "flattenAxisDisplayScale" };
|
|
BoxDisplayConfiguration config = EnsureConfiguration(control.gameObject, control.BoxDisplayConfig, HasPropertyOverrides(box, propertyPaths));
|
|
config.BoxMaterial = box.BoxMaterial;
|
|
config.BoxGrabbedMaterial = box.BoxGrabbedMaterial;
|
|
config.FlattenAxisDisplayScale = box.FlattenAxisDisplayScale;
|
|
control.BoxDisplayConfig = config;
|
|
}
|
|
|
|
private void MigrateLinks(BoundsControl control, BoundingBox box)
|
|
{
|
|
List<string> propertyPaths = new List<string> { "wireframeMaterial", "wireframeEdgeRadius", "wireframeShape", "showWireFrame" };
|
|
LinksConfiguration config = EnsureConfiguration(control.gameObject, control.LinksConfig, HasPropertyOverrides(box, propertyPaths));
|
|
config.WireframeMaterial = box.WireframeMaterial;
|
|
config.WireframeEdgeRadius = box.WireframeEdgeRadius;
|
|
config.WireframeShape = MigrateWireframeShape(box.WireframeShape);
|
|
config.ShowWireFrame = box.ShowWireFrame;
|
|
control.LinksConfig = config;
|
|
}
|
|
|
|
private void MigrateScaleHandles(BoundsControl control, BoundingBox box)
|
|
{
|
|
List<string> propertyPaths = new List<string> { "scaleHandleSlatePrefab", "showScaleHandles", "handleMaterial", "handleGrabbedMaterial",
|
|
"scaleHandlePrefab", "scaleHandleSize", "scaleHandleColliderPadding", "drawTetherWhenManipulating", "handlesIgnoreCollider"};
|
|
ScaleHandlesConfiguration config = EnsureConfiguration(control.gameObject, control.ScaleHandlesConfig, HasPropertyOverrides(box, propertyPaths));
|
|
config.HandleSlatePrefab = box.ScaleHandleSlatePrefab;
|
|
config.ShowScaleHandles = box.ShowScaleHandles;
|
|
config.HandleMaterial = box.HandleMaterial;
|
|
config.HandleGrabbedMaterial = box.HandleGrabbedMaterial;
|
|
config.HandlePrefab = box.ScaleHandlePrefab;
|
|
config.HandleSize = box.ScaleHandleSize;
|
|
config.ColliderPadding = box.ScaleHandleColliderPadding;
|
|
config.DrawTetherWhenManipulating = box.DrawTetherWhenManipulating;
|
|
config.HandlesIgnoreCollider = box.HandlesIgnoreCollider;
|
|
control.ScaleHandlesConfig = config;
|
|
}
|
|
|
|
private void MigrateRotationHandles(BoundsControl control, BoundingBox box)
|
|
{
|
|
List<string> propertyPaths = new List<string> { "rotationHandlePrefabColliderType", "showRotationHandleForX", "showRotationHandleForY",
|
|
"showRotationHandleForZ", "handleMaterial", "handleGrabbedMaterial", "rotationHandlePrefab", "rotationHandleSize", "rotateHandleColliderPadding",
|
|
"drawTetherWhenManipulating", "handlesIgnoreCollider"};
|
|
RotationHandlesConfiguration config = EnsureConfiguration(control.gameObject, control.RotationHandlesConfig, HasPropertyOverrides(box, propertyPaths));
|
|
config.HandlePrefabColliderType = MigrateRotationHandleColliderType(box.RotationHandlePrefabColliderType);
|
|
config.ShowHandleForX = box.ShowRotationHandleForX;
|
|
config.ShowHandleForY = box.ShowRotationHandleForY;
|
|
config.ShowHandleForZ = box.ShowRotationHandleForZ;
|
|
config.HandleMaterial = box.HandleMaterial;
|
|
config.HandleGrabbedMaterial = box.HandleGrabbedMaterial;
|
|
config.HandlePrefab = box.RotationHandlePrefab;
|
|
config.HandleSize = box.RotationHandleSize;
|
|
config.ColliderPadding = box.RotateHandleColliderPadding;
|
|
config.DrawTetherWhenManipulating = box.DrawTetherWhenManipulating;
|
|
config.HandlesIgnoreCollider = box.HandlesIgnoreCollider;
|
|
control.RotationHandlesConfig = config;
|
|
}
|
|
|
|
private void MigrateProximityEffect(BoundsControl control, BoundingBox box)
|
|
{
|
|
List<string> propertyPaths = new List<string> { "proximityEffectActive", "handleMediumProximity", "handleCloseProximity", "farScale",
|
|
"mediumScale", "closeScale", "farGrowRate", "mediumGrowRate", "closeGrowRate"};
|
|
ProximityEffectConfiguration config = EnsureConfiguration(control.gameObject, control.HandleProximityEffectConfig, HasPropertyOverrides(box, propertyPaths));
|
|
config.ProximityEffectActive = box.ProximityEffectActive;
|
|
config.ObjectMediumProximity = box.HandleMediumProximity;
|
|
config.ObjectCloseProximity = box.HandleCloseProximity;
|
|
config.FarScale = box.FarScale;
|
|
config.MediumScale = box.MediumScale;
|
|
config.CloseScale = box.CloseScale;
|
|
config.FarGrowRate = box.FarGrowRate;
|
|
config.MediumGrowRate = box.MediumGrowRate;
|
|
config.CloseGrowRate = box.CloseGrowRate;
|
|
control.HandleProximityEffectConfig = config;
|
|
}
|
|
|
|
#endregion Visuals Configuration Migration
|
|
|
|
private void MigrateAppBar(BoundingBox boundingBox, BoundsControl boundsControl)
|
|
{
|
|
// note: this might be expensive for larger scenes but we don't know where the appbar is
|
|
// placed in the hierarchy so we have to search the scene for it
|
|
AppBar[] appBars = Object.FindObjectsOfType<AppBar>();
|
|
for (int i = 0; i < appBars.Length; ++i)
|
|
{
|
|
AppBar appBar = appBars[i];
|
|
if (appBar.Target == boundingBox)
|
|
{
|
|
Undo.RecordObject(appBar, "BoundsControl migration: changed target of app bar.");
|
|
appBar.Target = boundsControl;
|
|
EditorUtility.SetDirty(appBar);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |