version 2.5.0

This commit is contained in:
Sean Lu
2024-12-06 15:44:37 +08:00
parent dfdcd0fd7f
commit 2bfa2ad4c7
966 changed files with 238216 additions and 77239 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3be1cbe32bf01284f9038c49e248393a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 67f99d00d315f5247a283247f1d1a046
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Android;
namespace VIVE.OpenXR.Toolkits.Spectator.Helper
{
public static class AndroidProcessHelper
{
private static AndroidJavaObject _activity;
public static AndroidJavaObject Activity
{
get
{
if (_activity != null)
{
return _activity;
}
var unityPlayer = new AndroidJavaClass(ANDROID_CLASS_UNITY_PLAYER);
_activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
return _activity;
}
}
// Reference: https://stackoverflow.com/q/58728596/10467387
public static IEnumerator RequestPermission(Dictionary<string, PermissionCallbacks> permissions)
{
var permissionGranted = Enumerable.Repeat(false, permissions.Count).ToList();
var permissionAsked = Enumerable.Repeat(false, permissions.Count).ToList();
var permissionAction = new List<Action>();
for (var i = 0; i < permissions.Count; i++)
{
int currentCount = i;
(string permission, PermissionCallbacks permissionCallbacks) = permissions.ElementAt(currentCount);
permissionAction.Add(() =>
{
permissionGranted[currentCount] = Permission.HasUserAuthorizedPermission(permission);
if (permissionGranted[currentCount] || permissionAsked[currentCount])
{
return;
}
Permission.RequestUserPermission(permission, permissionCallbacks);
permissionAsked[currentCount] = true;
});
}
for (var i = 0; i < permissions.Count;)
{
permissionAction[i].Invoke();
if (permissionAsked[i])
{
i++;
}
yield return new WaitForEndOfFrame();
}
}
public const string ANDROID_CLASS_UNITY_PLAYER = "com.unity3d.player.UnityPlayer";
public const string ANDROID_CLASS_MEDIA_STORE_IMAGE_MEDIA = "android.provider.MediaStore$Images$Media";
public const string ANDROID_CLASS_GRAPHICS_BITMAP_FACTORY = "android.graphics.BitmapFactory";
public const string ANDROID_CLASS_GRAPHICS_BITMAP_COMPRESS_FORMAT = "android.graphics.Bitmap$CompressFormat";
public const string ANDROID_CLASS_OS_ENVIRONMENT = "android.os.Environment";
public const string ANDROID_CLASS_OS_BUILD_VERSION = "android.os.Build$VERSION";
public const string ANDROID_CLASS_CONTENT_INTENT = "android.content.Intent";
public const string ANDROID_CLASS_CONTENT_VALUES = "android.content.ContentValues";
public const string ANDROID_CLASS_NET_URI = "android.net.Uri";
public const string JAVA_CLASS_IO_FILE = "java.io.File";
public const string JAVA_CLASS_IO_OUTPUTSTREAM = "java.io.OutputStream";
public const string JAVA_CLASS_IO_BYTEARRAYOUTPUTSTREAM = "java.io.ByteArrayOutputStream";
public const string JAVA_CLASS_IO_BYTEARRAYINPUTSTREAM = "java.io.ByteArrayInputStream";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52cbc1ad9d288d348854b4e81d8aa57c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace VIVE.OpenXR.Toolkits.Spectator.Helper
{
public static class EditorHelper
{
// Microsoft definition
// https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.conventions.backingfieldconvention?view=efcore-7.0#definition
private const string BackingFieldConventionPrefix = "<";
private const string BackingFieldConventionEndString = "k__BackingField";
public static void ShowDefaultInspector(SerializedObject obj)
{
EditorGUI.BeginChangeCheck();
obj.UpdateIfRequiredOrScript();
SerializedProperty iterator = obj.GetIterator();
for (bool enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
{
using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath))
{
string originalLabelText = iterator.name;
if (originalLabelText.EndsWith(BackingFieldConventionEndString))
{
string fixLabelText = Regex.Replace(
originalLabelText.Substring(
1,
originalLabelText.Length - 1 - BackingFieldConventionPrefix.Length -
BackingFieldConventionEndString.Length),
"([a-z])([A-Z])",
"$1 $2");
EditorGUILayout.PropertyField(
property: iterator,
label: new GUIContent(fixLabelText),
includeChildren: true);
}
else
{
EditorGUILayout.PropertyField(iterator, true);
}
}
}
obj.ApplyModifiedProperties();
EditorGUI.EndChangeCheck();
}
public static string PropertyName(string propertyName)
{
// Microsoft definition
// https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.conventions.backingfieldconvention?view=efcore-7.0#definition
return $"<{propertyName}>k__BackingField";
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2fb164a4bcc8f04e9337299f4c030d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,163 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace VIVE.OpenXR.Toolkits.Spectator.Helper
{
public static class IOProcessHelper
{
#if UNITY_ANDROID
/// <summary>
/// Get the path of pictures folder in external storage in Android.
/// </summary>
/// <returns>Return Pictures folder directory if get successfully, otherwise return empty string</returns>
public static string GetAndroidExternalStoragePicturesDirectory()
{
Debug.Log("GetAndroidExternalStoragePicturesDirectory called");
string path = string.Empty;
try
{
// Init the class in java code
using (AndroidJavaClass environment =
new AndroidJavaClass(AndroidProcessHelper.ANDROID_CLASS_OS_ENVIRONMENT))
{
// Call static method to get the path of pictures folder in external storage
path = environment.CallStatic<AndroidJavaObject>("getExternalStoragePublicDirectory",
environment.GetStatic<string>("DIRECTORY_PICTURES"))
.Call<string>("getAbsolutePath");
}
}
catch (Exception e)
{
Debug.LogError($"Error on getting the path of pictures folder in external storage in Android: {e}");
}
Debug.Log($"Get path in GetAndroidExternalStoragePicturesDirectory: {path}");
return path;
}
public static string GetAndroidPrimaryExternalStorageDirectory()
{
Debug.Log("GetAndroidPrimaryExternalStorageDirectory called");
string path = string.Empty;
try
{
using (AndroidJavaClass environment =
new AndroidJavaClass(AndroidProcessHelper.ANDROID_CLASS_OS_ENVIRONMENT))
{
path = environment.CallStatic<AndroidJavaObject>("getExternalStorageDirectory")
.Call<string>("getAbsolutePath");
}
}
catch (Exception e)
{
Debug.LogError($"Error on getting the path of pictures folder in external storage in Android: {e}");
}
Debug.Log($"Get path in GetAndroidPrimaryExternalStorageDirectory: {path}");
return path;
}
public static Dictionary<ExternalStorageType, string> GetAndroidAllExternalStorageDirectory()
{
Debug.Log("GetAndroidAllExternalStorageDirectory called");
// Get all available external file directories (emulated or removable (aka sd card))
AndroidJavaObject[] externalFilesDirectories =
AndroidProcessHelper.Activity.Call<AndroidJavaObject[]>("getExternalFilesDirs", (object)null);
var result = new Dictionary<ExternalStorageType, string>();
using (var environment = new AndroidJavaClass(AndroidProcessHelper.ANDROID_CLASS_OS_ENVIRONMENT))
{
foreach (var item in externalFilesDirectories)
{
string directory = item.Call<string>("getAbsolutePath");
Debug.Log($"Find the path in GetAndroidExternalStorageDirectory: {directory}");
if (environment.CallStatic<bool>("isExternalStorageRemovable", item))
{
result.Add(ExternalStorageType.Removable, directory);
}
else if (environment.CallStatic<bool>("isExternalStorageEmulated", item))
{
result.Add(ExternalStorageType.Emulated, directory);
}
}
}
return result;
}
public enum ExternalStorageType
{
Removable,
Emulated
}
#endif
public static Task SaveByteDataToDisk(byte[] bytes, string saveDirectory, string fileNameWithFileExtension)
{
Directory.CreateDirectory(saveDirectory);
try
{
string fullPath = Path.Combine(saveDirectory, fileNameWithFileExtension);
System.IO.File.WriteAllBytes(fullPath, bytes);
}
catch (Exception e)
{
Debug.LogError($"Error on writing byte data to disk: {e}");
}
return Task.CompletedTask;
}
public static byte[] OpenFile(string path)
{
if (!File.Exists(path))
{
Debug.LogError("File not exist: " + path);
return null;
}
byte[] data = File.ReadAllBytes(path);
if (data.Length == 0)
{
Debug.LogError("File is empty: " + path);
return null;
}
return data;
}
public static byte[] OpenJpeg(string path)
{
byte[] data = OpenFile(path);
if (data == null)
{
Debug.LogError("Open Jpeg error");
return null;
}
if (data[0] != 0xFF || data[1] != 0xD8)
{
Debug.LogError("File is not JPEG: " + path);
return null;
}
return data;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d28163e4ec0b605478774041d1222262
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,102 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditorInternal;
#endif
namespace VIVE.OpenXR.Toolkits.Spectator.Helper
{
public static class LayerMaskHelper
{
public static bool HasLayer(this LayerMask layerMask, int layer)
{
if (layerMask == (layerMask | (1 << layer)))
{
return true;
}
return false;
}
public static bool[] HasLayers(this LayerMask layerMask)
{
var hasLayers = new bool[32];
for (int i = 0; i < 32; i++)
{
if (layerMask == (layerMask | (1 << i)))
{
Debug.Log($"LayerMask.LayerToName = {LayerMask.LayerToName(i)}");
hasLayers[i] = true;
}
}
return hasLayers;
}
#if UNITY_EDITOR
public static class LayerMaskDrawer
{
public static int LayerMaskField(string label, int layermask)
{
return FieldToLayerMask(EditorGUILayout.MaskField(label, LayerMaskToField(layermask),
InternalEditorUtility.layers));
}
public static int LayerMaskField(Rect position, string label, int layermask)
{
return FieldToLayerMask(EditorGUI.MaskField(position, label, LayerMaskToField(layermask),
InternalEditorUtility.layers));
}
/// <summary>
/// Converts field LayerMask values to in game LayerMask values
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
private static int FieldToLayerMask(int field)
{
if (field == -1) return -1;
int mask = 0;
var layers = InternalEditorUtility.layers;
for (int c = 0; c < layers.Length; c++)
{
if ((field & (1 << c)) != 0)
{
mask |= 1 << LayerMask.NameToLayer(layers[c]);
}
else
{
mask &= ~(1 << LayerMask.NameToLayer(layers[c]));
}
}
return mask;
}
/// <summary>
/// Converts in game LayerMask values to field LayerMask values
/// </summary>
/// <param name="mask"></param>
/// <returns></returns>
private static int LayerMaskToField(int mask)
{
if (mask == -1) return -1;
int field = 0;
var layers = InternalEditorUtility.layers;
for (int c = 0; c < layers.Length; c++)
{
if ((mask & (1 << LayerMask.NameToLayer(layers[c]))) != 0)
{
field |= 1 << c;
}
}
return field;
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 05c260c7d911cff49b0333f5ba628420
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,516 @@
// Copyright HTC Corporation All Rights Reserved.
using System.IO;
using UnityEngine;
namespace VIVE.OpenXR.Toolkits.Spectator.Helper
{
public class SpectatorCameraHelper
{
# region Attribute value range
public const float VERTICAL_FOV_MIN = 10f;
public const float VERTICAL_FOV_MAX = 130f;
public const float FRUSTUM_LINE_WIDTH_MIN = .02f;
public const float FRUSTUM_LINE_WIDTH_MAX = .05f;
public const float FRUSTUM_CENTER_LINE_WIDTH_MIN = .01f;
public const float FRUSTUM_CENTER_LINE_WIDTH_MAX = .04f;
public const int PANORAMA_RESOLUTION_MIN = 512;
public const int PANORAMA_RESOLUTION_MAX = 4096;
public const int SMOOTH_CAMERA_MOVEMENT_MIN = 8;
public const int SMOOTH_CAMERA_MOVEMENT_MAX = 16;
public const float COMPARE_FLOAT_SUPER_SMALL_THRESHOLD = .001f;
public const float COMPARE_FLOAT_EXTRA_SMALL_THRESHOLD = .01f;
public const float COMPARE_FLOAT_SMALL_THRESHOLD = .1f;
public const float COMPARE_FLOAT_MEDIUM_THRESHOLD = 1f;
public const float COMPARE_FLOAT_LARGE_THRESHOLD = 10f;
public const float INTERVAL_SECOND_GET_SPECTATOR_HANDLER = .5f;
public const float MAX_SECOND_GET_SPECTATOR_HANDLER = 5f;
public const float INTERVAL_SECOND_INTERNAL_SPECTATOR_CAMERA = 1f;
public const float MAX_SECOND_GET_INTERNAL_SPECTATOR_CAMERA = 2f;
# endregion
public static readonly Vector3 SpectatorCameraSpherePrefabScaleDefault = new Vector3(.08f, .08f, .08f);
#region Attribute default value definition
public const int TEXTURE_WIDTH = 1920;
public const int TEXTURE_HEIGHT = 1080;
public const CameraSourceRef CAMERA_SOURCE_REF_DEFAULT = CameraSourceRef.Hmd;
public static readonly Vector3 PositionDefault = new Vector3(0f, 1.7f, 0f);
public static readonly Quaternion RotationDefault = Quaternion.identity;
public static readonly LayerMask LayerMaskDefault = -1;
public const bool IS_SMOOTH_CAMERA_MOVEMENT_DEFAULT = true;
public const int SMOOTH_CAMERA_MOVEMENT_SPEED_DEFAULT = 10;
public const bool IS_FRUSTUM_SHOWED_DEFAULT = false;
public const float VERTICAL_FOV_DEFAULT = 60f;
public const SpectatorCameraPanoramaResolution PANORAMA_RESOLUTION_DEFAULT =
SpectatorCameraPanoramaResolution._2048;
public const TextureProcessHelper.PictureOutputFormat PANORAMA_OUTPUT_FORMAT_DEFAULT =
TextureProcessHelper.PictureOutputFormat.PNG;
public const TextureProcessHelper.PanoramaType PANORAMA_TYPE_DEFAULT =
TextureProcessHelper.PanoramaType.Monoscopic;
public const float STEREO_SEPARATION_DEFAULT = 0.065f;
public const FrustumLineCount FRUSTUM_LINE_COUNT_DEFAULT =
FrustumLineCount.Four;
public const FrustumCenterLineCount FRUSTUM_CENTER_LINE_COUNT_DEFAULT =
FrustumCenterLineCount.Center;
public const float FRUSTUM_LINE_WIDTH_DEFAULT = .02f;
public const float FRUSTUM_CENTER_LINE_WIDTH_DEFAULT = .01f;
public const float FRUSTUM_LINE_BEGIN_DEFAULT = .3f;
// Define by Unity: https://docs.unity3d.com/ScriptReference/Camera.CalculateFrustumCorners.html
public const int FRUSTUM_OUT_CORNERS_COUNT = 4;
public static readonly Color LineColorDefault = Color.white;
public const string LINE_SHADER_NAME_DEFAULT = "UI/Default";
#if UNITY_EDITOR
public const bool IS_DEBUG_SPECTATOR_CAMERA = true;
#endif
#endregion
#region Frustum game object name definition
public const string FRUSTUM_LINE_ROOT_NAME_DEFAULT = "FrustumLines";
public const string FRUSTUM_LINE_NAME_PREFIX_DEFAULT = "Frustum";
public const string FRUSTUM_CENTER_LINE_ROOT_NAME_DEFAULT = "FrustumCenterLines";
public const string FRUSTUM_CENTER_LINE_NAME_PREFIX_DEFAULT = "FrustumCenter";
#endregion
#region Save file definition
public const string SAVE_PHOTO360_ALBUM_NAME = "Screenshots";
public const string ATTRIBUTE_FILE_PREFIX_NAME = "SpectatorCameraAttribute";
public const string ATTRIBUTE_FILE_EXTENSION = "json";
#endregion
/// <summary>
/// Load the attribute file from resource folder
/// </summary>
/// <param name="sceneName">The corresponding scene name of the attribute file</param>
/// <param name="gameObjectName">The corresponding gameObject name of the attribute file</param>
/// <param name="data">The attribute data which save in attribute file</param>
/// <returns>True if get the attribute data successfully. Otherwise, return false.</returns>
public static bool LoadAttributeFileFromResourcesFolder(
in string sceneName,
in string gameObjectName,
out SpectatorCameraAttribute data)
{
Debug.Log("Get spectator camera attribute at resources folder");
data = new SpectatorCameraAttribute();
TextAsset json;
// Name format: {PREFIX}_{SCENE NAME}_{GAME OBJECT NAME}.json
var fileName = GetSpectatorCameraAttributeFileNamePattern(sceneName, gameObjectName, false);
try
{
json = Resources.Load<TextAsset>(fileName);
}
catch (System.Exception e)
{
Debug.LogWarning("Get attribute data from resources folder fail:");
Debug.LogWarning($"{e}");
return false;
}
if (json == null)
{
Debug.LogWarning("The attribute data at resources folder is empty or null");
return false;
}
Debug.Log($"The attribute value is: {json.text}");
data = JsonUtility.FromJson<SpectatorCameraAttribute>(json.text);
if (data == null)
{
Debug.LogWarning("Convert attribute data from resources folder fail");
return false;
}
Debug.Log("Get attribute data from resources folder successful");
return true;
}
/// <summary>
/// Load the attribute file from full path
/// </summary>
/// <param name="fullPathWithFileNameAndExtension">Full path</param>
/// <param name="data">The attribute data which save in persistent folder</param>
/// <returns>True if get the attribute data successfully. Otherwise, return false.</returns>
public static bool LoadAttributeFileFromFolder(
string fullPathWithFileNameAndExtension,
out SpectatorCameraAttribute data)
{
Debug.Log($"Get spectator camera attribute at {fullPathWithFileNameAndExtension}");
return LoadAttributeData(fullPathWithFileNameAndExtension, out data);
}
/// <summary>
/// Load the attribute file from persistent folder
/// </summary>
/// <param name="sceneName">The corresponding scene name of the attribute file</param>
/// <param name="gameObjectName">The corresponding gameObject name of the attribute file</param>
/// <param name="data">The attribute data which save in persistent folder</param>
/// <returns>True if get the attribute data successfully. Otherwise, return false.</returns>
public static bool LoadAttributeFileFromPersistentFolder(
in string sceneName,
in string gameObjectName,
out SpectatorCameraAttribute data)
{
// Name format: {PREFIX}_{SCENE NAME}_{GAME OBJECT NAME}.json
var fileNameWithExtension = GetSpectatorCameraAttributeFileNamePattern(sceneName, gameObjectName);
var attributeFileDirectoryAndFileNameWithExtension =
Path.Combine(Application.persistentDataPath, fileNameWithExtension);
Debug.Log($"Get spectator camera attribute at {attributeFileDirectoryAndFileNameWithExtension}");
return LoadAttributeData(attributeFileDirectoryAndFileNameWithExtension, out data);
}
#if UNITY_EDITOR
/// <summary>
/// Save the spectator camera attribute as a JSON file to the resources folder located in the Unity project's assets folder.
/// </summary>
public static void SaveAttributeData2ResourcesFolder(
in string sceneName,
in string gameObjectName,
in SpectatorCameraAttribute data)
{
// Name format: {PREFIX}_{SCENE NAME}_{GAME OBJECT NAME}.json
var fileNameWithExtension = GetSpectatorCameraAttributeFileNamePattern(sceneName, gameObjectName);
string resourcesFolderPath = Path.Combine(Application.dataPath, "Resources");
SaveAttributeData(resourcesFolderPath, fileNameWithExtension, data);
}
#endif
/// <summary>
/// Save the spectator camera attribute as a JSON file to a persistent folder.
/// </summary>
public static void SaveAttributeData2PersistentFolder(
in string sceneName,
in string gameObjectName,
in SpectatorCameraAttribute data)
{
// Name format: {PREFIX}_{SCENE NAME}_{GAME OBJECT NAME}.json
var fileNameWithExtension = GetSpectatorCameraAttributeFileNamePattern(sceneName, gameObjectName);
SaveAttributeData(Application.persistentDataPath, fileNameWithExtension, data);
}
/// <summary>
/// Load the spectator camera attribute as a JSON file on disk.
/// </summary>
/// <param name="fullPathWithFileNameAndExtension">The path (string) that include directory, file name and extension</param>
/// <param name="data">The data of spectator camera attribute</param>
/// <returns>True if get the attribute data successfully. Otherwise, return false.</returns>
private static bool LoadAttributeData(
string fullPathWithFileNameAndExtension,
out SpectatorCameraAttribute data)
{
data = new SpectatorCameraAttribute();
string json;
try
{
json = File.ReadAllText(fullPathWithFileNameAndExtension);
}
catch (System.Exception e)
{
Debug.LogWarning($"Get attribute data from {fullPathWithFileNameAndExtension} failed: {e}");
return false;
}
if (string.IsNullOrEmpty(json))
{
Debug.LogWarning($"The attribute data at {fullPathWithFileNameAndExtension} is empty or null");
return false;
}
Debug.Log($"The attribute value is: {json}");
data = JsonUtility.FromJson<SpectatorCameraAttribute>(json);
if (data == null)
{
Debug.LogWarning($"Convert attribute data from {fullPathWithFileNameAndExtension} failed");
return false;
}
Debug.Log($"Get attribute data from {fullPathWithFileNameAndExtension} successful");
return true;
}
/// <summary>
/// Save the spectator camera attribute as a JSON file on disk.
/// </summary>
/// <param name="saveFileDirectory">The directory that the spectator camera attribute (JSON file) will save to</param>
/// <param name="saveFileNameWithExtension">The file name of the spectator camera attribute (JSON file)</param>
/// <param name="data">The data of spectator camera attribute</param>
private static void SaveAttributeData(
in string saveFileDirectory,
in string saveFileNameWithExtension,
in SpectatorCameraAttribute data)
{
if (string.IsNullOrEmpty(saveFileDirectory) || string.IsNullOrEmpty(saveFileNameWithExtension))
{
Debug.LogError("The saving file directory or name is null or empty");
return;
}
string fullPath = Path.Combine(saveFileDirectory, saveFileNameWithExtension);
// Convert to string format
string json = JsonUtility.ToJson(data);
// Make sure the file path is exist
if (!Directory.Exists(saveFileDirectory))
{
Directory.CreateDirectory(saveFileDirectory);
}
File.WriteAllText(fullPath, json);
Debug.Log($"The configuration save at {fullPath}");
}
public static string GetSpectatorCameraAttributeFileNamePattern(
in string sceneName,
in string gameObjectName,
bool withExtension = true)
{
var fileNamePattern = $"{ATTRIBUTE_FILE_PREFIX_NAME}_{sceneName}_{gameObjectName}";
if (withExtension)
{
fileNamePattern += $".{ATTRIBUTE_FILE_EXTENSION}";
}
return fileNamePattern;
}
/// <summary>
/// Check the panorama resolution is power of two or not. If not, convert to power of two.
/// </summary>
/// <param name="inputResolution">The panorama resolution</param>
/// <returns></returns>
public static int CheckAndConvertPanoramaResolution(in int inputResolution)
{
int result;
// Check is power of two
if ((inputResolution != 0) && ((inputResolution & (inputResolution - 1)) == 0))
{
result = Mathf.Clamp(inputResolution, PANORAMA_RESOLUTION_MIN, PANORAMA_RESOLUTION_MAX);
}
else
{
// If not power of two, convert to power of two
int clampInputValue = Mathf.Clamp(inputResolution, PANORAMA_RESOLUTION_MIN, PANORAMA_RESOLUTION_MAX);
var base2LogarithmOfClampInputValue = (int)System.Math.Log(clampInputValue, 2);
var base2LogarithmOfClampInputValueLowerStepValue =
(int)System.Math.Pow(2, base2LogarithmOfClampInputValue);
var base2LogarithmOfClampInputValueUpperStepValue =
(int)System.Math.Pow(2, base2LogarithmOfClampInputValue + 1);
// Check which one is closer
if (clampInputValue - base2LogarithmOfClampInputValueLowerStepValue <=
base2LogarithmOfClampInputValueUpperStepValue - clampInputValue)
{
result = base2LogarithmOfClampInputValueLowerStepValue;
}
else
{
result = base2LogarithmOfClampInputValueUpperStepValue;
}
}
return result;
}
#region Functions of changing camera culling mask
/// <summary>
/// Set the layer that the camera can watch
/// </summary>
/// <param name="targetCullingMask">The camera culling mask</param>
/// <param name="shownLayer">The layer number that you want to set the camera can watch</param>
/// <returns>The new camera culling mask after modify</returns>
public static LayerMask SetCameraVisualizationLayer(in LayerMask targetCullingMask, in int shownLayer)
{
LayerMask targetLayerMaskAfterModify = targetCullingMask;
targetLayerMaskAfterModify |= 1 << shownLayer;
return targetLayerMaskAfterModify;
}
/// <summary>
/// Set the layer that the camera cannot watch
/// </summary>
/// <param name="targetCullingMask">The camera culling mask</param>
/// <param name="hiddenLayer">The layer number that you want to set the camera cannot watch</param>
/// <returns>The new camera culling mask after modify</returns>
public static LayerMask SetCameraHiddenLayer(in LayerMask targetCullingMask, in int hiddenLayer)
{
LayerMask targetLayerMaskAfterModify = targetCullingMask;
targetLayerMaskAfterModify &= ~(1 << hiddenLayer);
return targetLayerMaskAfterModify;
}
/// <summary>
/// Inverse specific layer in camera culling mask
/// </summary>
/// <param name="targetCullingMask">The camera culling mask</param>
/// <param name="inverseLayer">The layer number that you want to inverse</param>
/// <returns>The new camera culling mask after modify</returns>
public static LayerMask InverseCameraLayer(in LayerMask targetCullingMask, in int inverseLayer)
{
LayerMask targetLayerMaskAfterModify = targetCullingMask;
targetLayerMaskAfterModify ^= 1 << inverseLayer;
return targetLayerMaskAfterModify;
}
#endregion
[System.Serializable]
public class SpectatorCameraAttribute
{
#region Serializable class field
public CameraSourceRef source;
public Vector3 position;
public Quaternion rotation;
public LayerMask layerMask;
public bool isSmoothCameraMovement;
public int smoothCameraMovementSpeed;
public bool isFrustumShowed;
public float verticalFov;
public SpectatorCameraPanoramaResolution panoramaResolution;
public TextureProcessHelper.PictureOutputFormat panoramaOutputFormat;
public TextureProcessHelper.PanoramaType panoramaOutputType;
public FrustumLineCount frustumLineCount;
public FrustumCenterLineCount frustumCenterLineCount;
public float frustumLineWidth;
public float frustumCenterLineWidth;
public Color frustumLineColor;
public Color frustumCenterLineColor;
#endregion
#region Constructor
public SpectatorCameraAttribute()
{
}
public SpectatorCameraAttribute(
CameraSourceRef source,
Vector3 position,
Quaternion rotation,
LayerMask layerMask,
bool isSmoothCameraMovement,
int smoothCameraMovementSpeed,
bool isFrustumShowed,
float verticalFov,
SpectatorCameraPanoramaResolution panoramaResolution,
TextureProcessHelper.PictureOutputFormat panoramaOutputFormat,
TextureProcessHelper.PanoramaType panoramaOutputType,
FrustumLineCount frustumLineCount,
FrustumCenterLineCount frustumCenterLineCount,
float frustumLineWidth,
float frustumCenterLineWidth,
Color frustumLineColor,
Color frustumCenterLineColor)
{
this.source = source;
this.position = position;
this.rotation = rotation;
this.layerMask = layerMask;
this.isSmoothCameraMovement = isSmoothCameraMovement;
this.smoothCameraMovementSpeed = smoothCameraMovementSpeed;
this.isFrustumShowed = isFrustumShowed;
this.verticalFov = verticalFov;
this.panoramaResolution = panoramaResolution;
this.panoramaOutputFormat = panoramaOutputFormat;
this.panoramaOutputType = panoramaOutputType;
this.frustumLineCount = frustumLineCount;
this.frustumCenterLineCount = frustumCenterLineCount;
this.frustumLineWidth = frustumLineWidth;
this.frustumCenterLineWidth = frustumCenterLineWidth;
this.frustumLineColor = frustumLineColor;
this.frustumCenterLineColor = frustumCenterLineColor;
}
#endregion
}
#region Enum definition
public enum CameraSourceRef
{
Hmd,
Tracker
}
public enum FrustumLineCount
{
None = 0,
Four = 4,
Eight = 8,
Sixteen = 16,
ThirtyTwo = 32,
OneTwentyEight = 128,
}
public enum FrustumCenterLineCount
{
None = 0,
Center = 1,
RuleOfThirds = 4,
CenterAndRuleOfThirds = 5,
}
public enum SpectatorCameraPanoramaResolution
{
_512 = 512,
_1024 = 1024,
_2048 = 2048,
_4096 = 4096
}
public enum AttributeFileLocation
{
ResourceFolder,
PersistentFolder
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3136b6864f4cb9c4da5234953d3a75b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09984648d0f364648af4f0ac5a03de55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,127 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEngine;
using VIVE.OpenXR.Toolkits.Spectator.Helper;
namespace VIVE.OpenXR.Toolkits.Spectator
{
/// <summary>
/// Name: ISpectatorCameraSetting.cs
/// Role: Contract
/// Responsibility: Define the setting attribute of the spectator camera.
/// </summary>
public interface ISpectatorCameraSetting
{
#region Property
/// <summary>
/// The struct UnityEngine.LayerMask defines which layer the camera can see or not.
/// </summary>
LayerMask LayerMask { get; set; }
/// <summary>
/// Whether or not to enable the feature of smoothing the spectator camera movement.
/// </summary>
bool IsSmoothCameraMovement { get; set; }
/// <summary>
/// The speed factor to control the smoothing impact.
/// </summary>
int SmoothCameraMovementSpeed { get; set; }
/// <summary>
/// True if visualize the spectator camera vertical FOV.
/// </summary>
bool IsFrustumShowed { get; set; }
/// <summary>
/// The spectator camera vertical FOV.
/// </summary>
float VerticalFov { get; set; }
/// <summary>
/// The panorama image resolution.
/// </summary>
SpectatorCameraHelper.SpectatorCameraPanoramaResolution PanoramaResolution { get; set; }
/// <summary>
/// The panorama image output format.
/// </summary>
TextureProcessHelper.PictureOutputFormat PanoramaOutputFormat { get; set; }
/// <summary>
/// The panorama types.
/// </summary>
TextureProcessHelper.PanoramaType PanoramaOutputType { get; set; }
/// <summary>
/// How many frustum lines will be shown?
/// </summary>
SpectatorCameraHelper.FrustumLineCount FrustumLineCount { get; set; }
/// <summary>
/// How many frustum center lines will be shown?
/// </summary>
SpectatorCameraHelper.FrustumCenterLineCount FrustumCenterLineCount { get; set; }
/// <summary>
/// Frustum line width.
/// </summary>
float FrustumLineWidth { get; set; }
/// <summary>
/// Frustum center line width.
/// </summary>
float FrustumCenterLineWidth { get; set; }
/// <summary>
/// Frustum line color.
/// </summary>
Color FrustumLineColor { get; set; }
/// <summary>
/// Frustum center line color.
/// </summary>
Color FrustumCenterLineColor { get; set; }
#endregion
#region Function
/// <summary>
/// Reset the spectator camera setting to the default value.
/// </summary>
void ResetSetting();
/// <summary>
/// Export the current spectator camera setting as a JSON file and then save it to the resource folder or persistent folder.
/// </summary>
/// <param name="attributeFileLocation">The enum SpectatorCameraHelper.AttributeFileLocation.</param>
void ExportSetting2JsonFile(in SpectatorCameraHelper.AttributeFileLocation attributeFileLocation);
/// <summary>
/// Load the setting (JSON) file via input full file path.
/// </summary>
/// <param name="jsonFilePath">The setting files full path (including file name and JSON extension).</param>
void LoadSettingFromJsonFile(in string jsonFilePath);
/// <summary>
/// Load the setting (JSON) file via input scene name, GameObject (hmd) name, and the file location (resource folder or persistent folder).
/// </summary>
/// <param name="sceneName">The scene name.</param>
/// <param name="gameObjectName">The GameObject name.</param>
/// <param name="attributeFileLocation"> The enum SpectatorCameraHelper.AttributeFileLocation.</param>
void LoadSettingFromJsonFile(
in string sceneName,
in string gameObjectName,
in SpectatorCameraHelper.AttributeFileLocation attributeFileLocation);
/// <summary>
/// Apply the spectator camera setting to the current component.
/// </summary>
/// <param name="data">The data you want to apply.</param>
void ApplyData(in SpectatorCameraHelper.SpectatorCameraAttribute data);
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 913ccea163eb50d4e911f0e8cadaadeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,748 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using VIVE.OpenXR.Toolkits.Spectator.Helper;
namespace VIVE.OpenXR.Toolkits.Spectator
{
/// <summary>
/// Name: SpectatorCameraManager.Editor.cs
/// Role: General script use in Unity Editor only
/// Responsibility: Display the SpectatorCameraManager.cs in Unity Inspector
/// </summary>
public partial class SpectatorCameraManager
{
[SerializeField] private Material spectatorCameraViewMaterial;
/// <summary>
/// Material that show the spectator camera view
/// </summary>
private Material SpectatorCameraViewMaterial
{
// get; private set;
get => spectatorCameraViewMaterial;
set
{
spectatorCameraViewMaterial = value;
if (SpectatorCameraBased && value)
{
SpectatorCameraBased.SpectatorCameraViewMaterial = value;
}
}
}
#if UNITY_EDITOR
[field: SerializeField] private bool IsShowHmdPart { get; set; }
[field: SerializeField] private bool IsShowTrackerPart { get; set; }
[field: SerializeField] private bool IsRequireLoadJsonFile { get; set; }
[CustomEditor(typeof(SpectatorCameraManager))]
public class SpectatorCameraManagerEditor : UnityEditor.Editor
{
private static readonly Color HighlightRegionBackgroundColor = new Color(.2f, .2f, .2f, .1f);
private SerializedProperty IsShowHmdPart { get; set; }
private SerializedProperty IsShowTrackerPart { get; set; }
private SerializedProperty IsRequireLoadJsonFile { get; set; }
private List<string> JsonFileList { get; set; }
private Vector2 JsonFileScrollViewVector { get; set; }
private SerializedProperty IsSmoothCameraMovement { get; set; }
private SerializedProperty SmoothCameraMovementSpeed { get; set; }
private SerializedProperty PanoramaResolution { get; set; }
private SerializedProperty PanoramaOutputFormat { get; set; }
private SerializedProperty PanoramaOutputType { get; set; }
private SerializedProperty SpectatorCameraPrefab { get; set; }
private void OnEnable()
{
IsShowHmdPart = serializedObject.FindProperty(EditorHelper.PropertyName("IsShowHmdPart"));
IsShowTrackerPart = serializedObject.FindProperty(EditorHelper.PropertyName("IsShowTrackerPart"));
IsRequireLoadJsonFile =
serializedObject.FindProperty(EditorHelper.PropertyName("IsRequireLoadJsonFile"));
JsonFileList = new List<string>();
JsonFileScrollViewVector = Vector2.zero;
IsSmoothCameraMovement =
serializedObject.FindProperty(EditorHelper.PropertyName("IsSmoothCameraMovement"));
SmoothCameraMovementSpeed =
serializedObject.FindProperty(EditorHelper.PropertyName("SmoothCameraMovementSpeed"));
PanoramaResolution = serializedObject.FindProperty(EditorHelper.PropertyName("PanoramaResolution"));
PanoramaOutputFormat = serializedObject.FindProperty(EditorHelper.PropertyName("PanoramaOutputFormat"));
PanoramaOutputType = serializedObject.FindProperty(EditorHelper.PropertyName("PanoramaOutputType"));
SpectatorCameraPrefab =
serializedObject.FindProperty(EditorHelper.PropertyName("SpectatorCameraPrefab"));
}
public override void OnInspectorGUI()
{
// Just return if not "SpectatorCameraManager" class
if (!(target is SpectatorCameraManager))
{
return;
}
serializedObject.Update();
DrawGUI();
serializedObject.ApplyModifiedProperties();
}
private void DrawGUI()
{
#region GUIStyle
var labelStyle = new GUIStyle()
{
richText = true,
alignment = TextAnchor.MiddleCenter,
normal = new GUIStyleState
{
textColor = EditorGUIUtility.isProSkin ? Color.green : Color.black
}
};
var resetButtonStyle = new GUIStyle(GUI.skin.button)
{
fontStyle = FontStyle.Bold,
normal = new GUIStyleState
{
textColor = EditorGUIUtility.isProSkin ? Color.yellow : Color.red
},
hover = new GUIStyleState
{
textColor = Color.red
},
active = new GUIStyleState
{
textColor = Color.cyan
},
};
var boldButtonStyle = new GUIStyle(GUI.skin.button)
{
fontStyle = FontStyle.Bold
};
#endregion
var script = (SpectatorCameraManager)target;
// Button for reset value
if (GUILayout.Button("Reset to default value", resetButtonStyle))
{
Undo.RecordObject(target, "Reset SpectatorCameraManager to default value");
EditorUtility.SetDirty(target);
script.ResetSetting();
}
// Button for export setting
if (GUILayout.Button("Export Spectator Camera HMD Setting", boldButtonStyle))
{
script.ExportSetting2JsonFile(SpectatorCameraHelper.AttributeFileLocation.ResourceFolder);
AssetDatabase.Refresh();
}
#region Load Setting From JSON File
GUILayout.BeginHorizontal();
EditorGUI.BeginDisabledGroup(IsRequireLoadJsonFile.boolValue);
if (GUILayout.Button("Load Setting From JSON File in Resources Folder", boldButtonStyle) ||
IsRequireLoadJsonFile.boolValue)
{
IsRequireLoadJsonFile.boolValue = true;
var searchPattern =
$"{SpectatorCameraHelper.ATTRIBUTE_FILE_PREFIX_NAME}*.{SpectatorCameraHelper.ATTRIBUTE_FILE_EXTENSION}";
if (JsonFileList == null)
{
JsonFileList = new List<string>();
}
JsonFileList.Clear();
var dir = new DirectoryInfo(Path.Combine(Application.dataPath, "Resources"));
var files = dir.GetFiles(searchPattern);
foreach (var item in files)
{
JsonFileList.Add(item.Name);
}
if (JsonFileList.Count == 0)
{
Debug.Log(
"Can't find any JSON file related to the spectator camera setting in the Resources folder.");
IsRequireLoadJsonFile.boolValue = false;
}
}
EditorGUI.EndDisabledGroup();
if (IsRequireLoadJsonFile.boolValue)
{
if (GUILayout.Button("Cancel"))
{
IsRequireLoadJsonFile.boolValue = false;
}
}
GUILayout.EndHorizontal();
if (IsRequireLoadJsonFile.boolValue)
{
Rect r = EditorGUILayout.BeginVertical();
JsonFileScrollViewVector = EditorGUILayout.BeginScrollView(JsonFileScrollViewVector,
GUILayout.Width(r.width),
GUILayout.Height(80));
for (int i = 0; i < JsonFileList.Count; i++)
{
if (GUILayout.Button(JsonFileList[i]))
{
var path = Path.Combine(
System.IO.Path.Combine(Application.dataPath, "Resources"),
JsonFileList[i]);
Undo.RecordObject(target, $"Load {JsonFileList[i]} setting to {target.name} SpectatorCameraManager");
EditorUtility.SetDirty(target);
script.LoadSettingFromJsonFile(path);
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
#endregion
EditorGUILayout.LabelField("\n");
// Spectator camera prefab
EditorGUILayout.PropertyField(SpectatorCameraPrefab, new GUIContent("Spectator Camera Prefab"));
if (SpectatorCameraPrefab.objectReferenceValue != null &&
PrefabUtility.GetPrefabAssetType(SpectatorCameraPrefab.objectReferenceValue) ==
PrefabAssetType.NotAPrefab)
{
// The assign object is scene object
Debug.Log("Please assign the object as prefab only.");
SpectatorCameraPrefab.objectReferenceValue = null;
}
EditorGUILayout.LabelField("\n");
EditorGUILayout.LabelField("<b>[ General Setting ]</b>", labelStyle);
// Setting of spectator camera reference source
// EditorGUILayout.PropertyField(CameraSourceRef, new GUIContent("Camera Source"));
EditorGUI.BeginChangeCheck();
var currentCameraSourceRef = (SpectatorCameraHelper.CameraSourceRef)
EditorGUILayout.EnumPopup("Camera Source", script.CameraSourceRef);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager CameraSourceRef");
EditorUtility.SetDirty(target);
script.CameraSourceRef = currentCameraSourceRef;
}
#region Tracker Region
if (script.CameraSourceRef == SpectatorCameraHelper.CameraSourceRef.Tracker)
{
EditorGUI.BeginChangeCheck();
var currentFollowSpectatorCameraTracker = EditorGUILayout.ObjectField(
"Tracker",
script.FollowSpectatorCameraTracker,
typeof(SpectatorCameraTracker),
true) as SpectatorCameraTracker;
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager FollowSpectatorCameraTracker");
EditorUtility.SetDirty(target);
script.FollowSpectatorCameraTracker = currentFollowSpectatorCameraTracker;
}
if (script.FollowSpectatorCameraTracker == null)
{
// The assign object is null
EditorGUILayout.HelpBox("Please assign the SpectatorCameraTracker", MessageType.Info, false);
}
else if (PrefabUtility.GetPrefabAssetType(script.FollowSpectatorCameraTracker) !=
PrefabAssetType.NotAPrefab)
{
// Don't allow assign object is prefab
Debug.Log("Please assign the scene object.");
script.FollowSpectatorCameraTracker = null;
}
else
{
// The assign object is scene object => ok
EditorGUILayout.LabelField("\n");
IsShowTrackerPart.boolValue =
EditorGUILayout.Foldout(IsShowTrackerPart.boolValue, "Tracker Setting");
if (IsShowTrackerPart.boolValue)
{
// If show the tracker setting
Rect r = EditorGUILayout.BeginVertical();
SpectatorCameraTracker trackerObject = script.FollowSpectatorCameraTracker;
if (trackerObject != null)
{
EditorGUILayout.HelpBox(
$"You are now editing the tracker setting in \"{trackerObject.gameObject.name}\" GameObject",
MessageType.Info,
true);
EditorGUI.BeginChangeCheck();
var currentTrackerObjectLayerMask =
LayerMaskHelper.LayerMaskDrawer.LayerMaskField("Camera Layer Mask",
trackerObject.LayerMask);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} LayerMask");
EditorUtility.SetDirty(trackerObject);
trackerObject.LayerMask = currentTrackerObjectLayerMask;
}
EditorGUI.BeginChangeCheck();
var currentTrackerObjectIsSmoothCameraMovement=
EditorGUILayout.Toggle("Enable Smoothing Camera Movement",
trackerObject.IsSmoothCameraMovement);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} IsSmoothCameraMovement");
EditorUtility.SetDirty(trackerObject);
trackerObject.IsSmoothCameraMovement = currentTrackerObjectIsSmoothCameraMovement;
}
if (trackerObject.IsSmoothCameraMovement)
{
EditorGUILayout.LabelField("\n");
EditorGUILayout.LabelField("<b>[ Smooth Camera Movement Speed Setting ]</b>",
labelStyle);
EditorGUI.BeginChangeCheck();
var currentTrackerObjectSmoothCameraMovementSpeed =
EditorGUILayout.IntSlider(
new GUIContent("Speed of Smoothing Camera Movement"),
trackerObject.SmoothCameraMovementSpeed,
SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_MIN,
SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} SmoothCameraMovementSpeed");
EditorUtility.SetDirty(trackerObject);
trackerObject.SmoothCameraMovementSpeed = currentTrackerObjectSmoothCameraMovementSpeed;
}
EditorGUILayout.LabelField("\n");
}
EditorGUI.BeginChangeCheck();
// Spectator camera frustum show/hide
var currentTrackerObjectIsFrustumShowed =
EditorGUILayout.Toggle("Enable Camera FOV Frustum", trackerObject.IsFrustumShowed);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} IsFrustumShowed");
EditorUtility.SetDirty(trackerObject);
trackerObject.IsFrustumShowed = currentTrackerObjectIsFrustumShowed;
}
EditorGUILayout.LabelField("\n");
#region VerticalFov
EditorGUILayout.LabelField("<b>[ Vertical FOV Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
var currentTrackerObjectVerticalFov = EditorGUILayout.Slider(
"Vertical FOV",
trackerObject.VerticalFov,
SpectatorCameraHelper.VERTICAL_FOV_MIN,
SpectatorCameraHelper.VERTICAL_FOV_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} VerticalFov");
EditorUtility.SetDirty(trackerObject);
trackerObject.VerticalFov = currentTrackerObjectVerticalFov;
}
#endregion
EditorGUILayout.LabelField("\n");
#region Setting related to panorama capturing of spectator camera
// Panorama resolution
EditorGUILayout.LabelField("<b>[ Panorama Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
// Panorama output resolution
var currentTrackerObjectPanoramaResolution =
(SpectatorCameraHelper.SpectatorCameraPanoramaResolution)
EditorGUILayout.EnumPopup("Resolution", trackerObject.PanoramaResolution);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} PanoramaResolution");
EditorUtility.SetDirty(trackerObject);
trackerObject.PanoramaResolution = currentTrackerObjectPanoramaResolution;
}
EditorGUI.BeginChangeCheck();
// Panorama output format
var currentTrackerObjectPanoramaOutputFormat = (TextureProcessHelper.PictureOutputFormat)
EditorGUILayout.EnumPopup("Output Format", trackerObject.PanoramaOutputFormat);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} PanoramaOutputFormat");
EditorUtility.SetDirty(trackerObject);
trackerObject.PanoramaOutputFormat = currentTrackerObjectPanoramaOutputFormat;
}
EditorGUI.BeginChangeCheck();
// Panorama output type
var currentTrackerObjectPanoramaOutputType = (TextureProcessHelper.PanoramaType)
EditorGUILayout.EnumPopup("Output Type", trackerObject.PanoramaOutputType);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} PanoramaOutputType");
EditorUtility.SetDirty(trackerObject);
trackerObject.PanoramaOutputType = currentTrackerObjectPanoramaOutputType;
}
#endregion
EditorGUILayout.LabelField("\n");
#region Setting related to frustum
if (trackerObject.IsFrustumShowed)
{
EditorGUILayout.LabelField("<b>[ Frustum Setting ]</b>",
labelStyle);
#region Count of frustum and frustum center line
EditorGUI.BeginChangeCheck();
var currentTrackerObjectFrustumLineCount = (SpectatorCameraHelper.FrustumLineCount)
EditorGUILayout.EnumPopup("Frustum Line Total", trackerObject.FrustumLineCount);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} FrustumLineCount");
EditorUtility.SetDirty(trackerObject);
trackerObject.FrustumLineCount = currentTrackerObjectFrustumLineCount;
}
EditorGUI.BeginChangeCheck();
var currentTrackerObjectFrustumCenterLineCount =
(SpectatorCameraHelper.FrustumCenterLineCount)
EditorGUILayout.EnumPopup("Frustum Center Line Total",
trackerObject.FrustumCenterLineCount);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} FrustumCenterLineCount");
EditorUtility.SetDirty(trackerObject);
trackerObject.FrustumCenterLineCount = currentTrackerObjectFrustumCenterLineCount;
}
#endregion
EditorGUILayout.LabelField("\n");
#region Width of frustum and frustum center line
EditorGUI.BeginChangeCheck();
var currentTrackerObjectFrustumLineWidth =
EditorGUILayout.Slider(
"Frustum Line Width",
trackerObject.FrustumLineWidth,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} FrustumLineWidth");
EditorUtility.SetDirty(trackerObject);
trackerObject.FrustumLineWidth = currentTrackerObjectFrustumLineWidth;
}
EditorGUI.BeginChangeCheck();
var currentTrackerObjectFrustumCenterLineWidth =
EditorGUILayout.Slider(
"Frustum Center Line Width",
trackerObject.FrustumCenterLineWidth,
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} FrustumCenterLineWidth");
EditorUtility.SetDirty(trackerObject);
trackerObject.FrustumCenterLineWidth = currentTrackerObjectFrustumCenterLineWidth;
}
#endregion
EditorGUILayout.LabelField("\n");
#region Material of frustum and frustum center line
EditorGUI.BeginChangeCheck();
var currentTrackerObjectFrustumLineColor = EditorGUILayout.ColorField(
"Frustum Line Color", trackerObject.FrustumLineColor);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} FrustumLineColor");
EditorUtility.SetDirty(trackerObject);
trackerObject.FrustumLineColor = currentTrackerObjectFrustumLineColor;
}
EditorGUI.BeginChangeCheck();
var currentTrackerObjectFrustumCenterLineColor = EditorGUILayout.ColorField(
"Frustum Center Line Color",
trackerObject.FrustumCenterLineColor);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(trackerObject, $"Modified {trackerObject.name} FrustumCenterLineColor");
EditorUtility.SetDirty(trackerObject);
trackerObject.FrustumCenterLineColor = currentTrackerObjectFrustumCenterLineColor;
}
#endregion
}
#endregion
EditorGUILayout.EndVertical();
r = new Rect(r.x, r.y, r.width, r.height);
EditorGUI.DrawRect(r, HighlightRegionBackgroundColor);
EditorGUILayout.LabelField("\n");
}
}
}
}
#endregion
#region HMD Region
IsShowHmdPart.boolValue = EditorGUILayout.Foldout(IsShowHmdPart.boolValue, "HMD Setting");
if (IsShowHmdPart.boolValue)
{
Rect r = EditorGUILayout.BeginVertical();
EditorGUI.BeginChangeCheck();
// Setting of spectator camera layer mask
var currentLayerMask =
LayerMaskHelper.LayerMaskDrawer.LayerMaskField("Camera Layer Mask", script.LayerMask);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager LayerMask");
EditorUtility.SetDirty(target);
script.LayerMask = currentLayerMask;
}
// Setting of smooth spectator camera movement
EditorGUILayout.PropertyField(IsSmoothCameraMovement,
new GUIContent("Enable Smoothing Camera Movement"));
if (IsSmoothCameraMovement.boolValue)
{
EditorGUILayout.LabelField("\n");
EditorGUILayout.LabelField("<b>[ Smooth Camera Movement Speed Setting ]</b>", labelStyle);
// Setting of smooth spectator camera movement speed
EditorGUILayout.IntSlider(
SmoothCameraMovementSpeed,
SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_MIN,
SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_MAX,
"Speed of Smoothing Camera Movement");
EditorGUILayout.LabelField("\n");
}
EditorGUI.BeginChangeCheck();
// Spectator camera frustum show/hide
var currentIsFrustumShowed =
EditorGUILayout.Toggle("Enable Camera FOV Frustum", script.IsFrustumShowed);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager IsFrustumShowed");
EditorUtility.SetDirty(target);
script.IsFrustumShowed = currentIsFrustumShowed;
}
EditorGUILayout.LabelField("\n");
#region VerticalFov
EditorGUILayout.LabelField("<b>[ Vertical FOV Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
// FOV
var currentVerticalFov = EditorGUILayout.Slider(
"Vertical FOV",
script.VerticalFov,
SpectatorCameraHelper.VERTICAL_FOV_MIN,
SpectatorCameraHelper.VERTICAL_FOV_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager VerticalFov");
EditorUtility.SetDirty(target);
script.VerticalFov = currentVerticalFov;
}
#endregion
EditorGUILayout.LabelField("\n");
#region Setting related to panorama capturing of spectator camera
// Panorama resolution
EditorGUILayout.LabelField("<b>[ Panorama Setting ]</b>", labelStyle);
EditorGUILayout.PropertyField(PanoramaResolution, new GUIContent("Resolution"));
// Panorama output format
EditorGUILayout.PropertyField(PanoramaOutputFormat, new GUIContent("Output Format"));
// Panorama output type
EditorGUILayout.PropertyField(PanoramaOutputType, new GUIContent("Output Type"));
#endregion
#region Setting related to frustum
if (script.IsFrustumShowed)
{
EditorGUILayout.LabelField("\n");
EditorGUILayout.LabelField("<b>[ Frustum Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
// Count of frustum line
var currentFrustumLineCount = (SpectatorCameraHelper.FrustumLineCount)
EditorGUILayout.EnumPopup("Frustum Line Total", script.FrustumLineCount);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager FrustumLineCount");
EditorUtility.SetDirty(target);
script.FrustumLineCount = currentFrustumLineCount;
}
EditorGUI.BeginChangeCheck();
// Count of frustum center line
var currentFrustumCenterLineCount = (SpectatorCameraHelper.FrustumCenterLineCount)
EditorGUILayout.EnumPopup("Frustum Center Line Total", script.FrustumCenterLineCount);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager FrustumCenterLineCount");
EditorUtility.SetDirty(target);
script.FrustumCenterLineCount = currentFrustumCenterLineCount;
}
EditorGUILayout.LabelField("\n");
EditorGUI.BeginChangeCheck();
// Width of frustum line
var currentFrustumLineWidth =
EditorGUILayout.Slider(
"Frustum Line Width",
script.FrustumLineWidth,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager FrustumLineWidth");
EditorUtility.SetDirty(target);
script.FrustumLineWidth = currentFrustumLineWidth;
}
EditorGUI.BeginChangeCheck();
// Width of frustum center line
var currentFrustumCenterLineWidth =
EditorGUILayout.Slider(
"Frustum Center Line Width",
script.FrustumCenterLineWidth,
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager FrustumCenterLineWidth");
EditorUtility.SetDirty(target);
script.FrustumCenterLineWidth = currentFrustumCenterLineWidth;
}
EditorGUILayout.LabelField("\n");
EditorGUI.BeginChangeCheck();
// Color of frustum line
var currentFrustumLineColor = EditorGUILayout.ColorField(
"Frustum Line Color", script.FrustumLineColor);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager FrustumLineColor");
EditorUtility.SetDirty(target);
script.FrustumLineColor = currentFrustumLineColor;
}
EditorGUI.BeginChangeCheck();
// Color of frustum center line
var currentFrustumCenterLineColor = EditorGUILayout.ColorField(
"Frustum Center Line Color",
script.FrustumCenterLineColor);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager FrustumCenterLineColor");
EditorUtility.SetDirty(target);
script.FrustumCenterLineColor = currentFrustumCenterLineColor;
}
}
#endregion
EditorGUILayout.EndVertical();
r = new Rect(r.x, r.y, r.width, r.height);
EditorGUI.DrawRect(r, HighlightRegionBackgroundColor);
}
#endregion
EditorGUILayout.LabelField("\n");
#region Test 360 Output
EditorGUILayout.LabelField("<b>[ Debug Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
var currentSpectatorCameraViewMaterial = EditorGUILayout.ObjectField(
"Spectator Camera View Material",
script.SpectatorCameraViewMaterial,
typeof(Material),
false) as Material;
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Modified SpectatorCameraManager SpectatorCameraViewMaterial");
EditorUtility.SetDirty(target);
script.SpectatorCameraViewMaterial = currentSpectatorCameraViewMaterial;
}
EditorGUILayout.HelpBox("Test - Output 360 photo", MessageType.Info, true);
if (GUILayout.Button("Test - Output 360 photo"))
{
script.CaptureSpectatorCamera360Photo();
}
#endregion
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d5a03d623c31c2344a1f99005572762a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fda46617f9e447e47863938d3006d5bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,374 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using VIVE.OpenXR.Toolkits.Spectator.Helper;
namespace VIVE.OpenXR.Toolkits.Spectator
{
/// <summary>
/// Name: SpectatorCameraTracker.Editor.cs
/// Role: General script use in Unity Editor only
/// Responsibility: Display the SpectatorCameraTracker.cs in Unity Inspector
/// </summary>
public partial class SpectatorCameraTracker
{
[field: SerializeField] private bool IsRequireLoadJsonFile { get; set; }
[CustomEditor(typeof(SpectatorCameraTracker))]
public class SpectatorCameraTrackerSettingEditor : UnityEditor.Editor
{
private SerializedProperty IsRequireLoadJsonFile { get; set; }
private List<string> JsonFileList { get; set; }
private Vector2 JsonFileScrollViewVector { get; set; }
private SerializedProperty IsSmoothCameraMovement { get; set; }
private SerializedProperty SmoothCameraMovementSpeed { get; set; }
private SerializedProperty PanoramaResolution { get; set; }
private SerializedProperty PanoramaOutputFormat { get; set; }
private SerializedProperty PanoramaOutputType { get; set; }
private void OnEnable()
{
IsRequireLoadJsonFile =
serializedObject.FindProperty(EditorHelper.PropertyName("IsRequireLoadJsonFile"));
JsonFileList = new List<string>();
JsonFileScrollViewVector = Vector2.zero;
IsSmoothCameraMovement =
serializedObject.FindProperty(EditorHelper.PropertyName("IsSmoothCameraMovement"));
SmoothCameraMovementSpeed =
serializedObject.FindProperty(EditorHelper.PropertyName("SmoothCameraMovementSpeed"));
PanoramaResolution = serializedObject.FindProperty(EditorHelper.PropertyName("PanoramaResolution"));
PanoramaOutputFormat = serializedObject.FindProperty(EditorHelper.PropertyName("PanoramaOutputFormat"));
PanoramaOutputType = serializedObject.FindProperty(EditorHelper.PropertyName("PanoramaOutputType"));
}
public override void OnInspectorGUI()
{
// Just return if not "SpectatorCameraTrackerSetting" class
if (!(target is SpectatorCameraTracker))
{
return;
}
serializedObject.Update();
DrawGUI();
serializedObject.ApplyModifiedProperties();
}
private void DrawGUI()
{
#region GUIStyle
var labelStyle = new GUIStyle()
{
richText = true,
alignment = TextAnchor.MiddleCenter,
normal = new GUIStyleState
{
textColor = EditorGUIUtility.isProSkin ? Color.green : Color.black
}
};
var resetButtonStyle = new GUIStyle(GUI.skin.button)
{
fontStyle = FontStyle.Bold,
normal = new GUIStyleState
{
textColor = EditorGUIUtility.isProSkin ? Color.yellow : Color.red
},
hover = new GUIStyleState
{
textColor = Color.red
},
active = new GUIStyleState
{
textColor = Color.cyan
},
};
var boldButtonStyle = new GUIStyle(GUI.skin.button)
{
fontStyle = FontStyle.Bold
};
#endregion
var script = (SpectatorCameraTracker)target;
// Button for reset value
if (GUILayout.Button("Reset to default value", resetButtonStyle))
{
Undo.RecordObject(target, $"Reset {target.name} SpectatorCameraTracker to default value");
EditorUtility.SetDirty(target);
script.ResetSetting();
}
// Button for export setting
if (GUILayout.Button("Export Spectator Camera Tracker Setting", boldButtonStyle))
{
script.ExportSetting2JsonFile(SpectatorCameraHelper.AttributeFileLocation.ResourceFolder);
AssetDatabase.Refresh();
}
#region Load setting from JSON file
GUILayout.BeginHorizontal();
EditorGUI.BeginDisabledGroup(IsRequireLoadJsonFile.boolValue);
if (GUILayout.Button("Load Setting From JSON File in Resources Folder", boldButtonStyle) ||
IsRequireLoadJsonFile.boolValue)
{
IsRequireLoadJsonFile.boolValue = true;
var searchPattern =
$"{SpectatorCameraHelper.ATTRIBUTE_FILE_PREFIX_NAME}*.{SpectatorCameraHelper.ATTRIBUTE_FILE_EXTENSION}";
if (JsonFileList == null)
{
JsonFileList = new List<string>();
}
JsonFileList.Clear();
var dir = new DirectoryInfo(Path.Combine(Application.dataPath, "Resources"));
var files = dir.GetFiles(searchPattern);
foreach (var item in files)
{
JsonFileList.Add(item.Name);
}
if (JsonFileList.Count == 0)
{
Debug.Log(
"Can't find any JSON file related to the spectator camera setting in the Resources folder.");
IsRequireLoadJsonFile.boolValue = false;
}
}
EditorGUI.EndDisabledGroup();
if (IsRequireLoadJsonFile.boolValue)
{
if (GUILayout.Button("Cancel"))
{
IsRequireLoadJsonFile.boolValue = false;
}
}
GUILayout.EndHorizontal();
if (IsRequireLoadJsonFile.boolValue)
{
Rect r = EditorGUILayout.BeginVertical();
JsonFileScrollViewVector = EditorGUILayout.BeginScrollView(JsonFileScrollViewVector,
GUILayout.Width(r.width),
GUILayout.Height(80));
for (int i = 0; i < JsonFileList.Count; i++)
{
if (GUILayout.Button(JsonFileList[i]))
{
var path = Path.Combine(
Path.Combine(Application.dataPath, "Resources"),
JsonFileList[i]);
Undo.RecordObject(target, $"Load {JsonFileList[i]} setting to {target.name} SpectatorCameraTracker");
EditorUtility.SetDirty(target);
script.LoadSettingFromJsonFile(path);
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
#endregion
EditorGUILayout.LabelField("\n");
#region Setting related to spectator camera and its frustum show/hide
EditorGUILayout.LabelField("<b>[ General Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
// Setting of spectator camera layer mask
var currentLayerMask =
LayerMaskHelper.LayerMaskDrawer.LayerMaskField("Camera Layer Mask", script.LayerMask);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker LayerMask");
EditorUtility.SetDirty(target);
script.LayerMask = currentLayerMask;
}
// Setting of smooth spectator camera movement
EditorGUILayout.PropertyField(IsSmoothCameraMovement,
new GUIContent("Enable Smoothing Camera Movement"));
if (IsSmoothCameraMovement.boolValue)
{
EditorGUILayout.LabelField("\n");
EditorGUILayout.LabelField("<b>[ Smooth Camera Movement Speed Setting ]</b>", labelStyle);
EditorGUILayout.IntSlider(
SmoothCameraMovementSpeed,
SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_MIN,
SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_MAX,
"Speed of Smoothing Camera Movement");
EditorGUILayout.LabelField("\n");
}
EditorGUI.BeginChangeCheck();
// Spectator camera frustum show/hide
var currentIsFrustumShowed =
EditorGUILayout.Toggle("Enable Camera FOV Frustum", script.IsFrustumShowed);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker IsFrustumShowed");
EditorUtility.SetDirty(target);
script.IsFrustumShowed = currentIsFrustumShowed;
}
#endregion
EditorGUILayout.LabelField("\n");
#region VerticalFov
EditorGUILayout.LabelField("<b>[ Vertical FOV Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
// FOV
var currentVerticalFov = EditorGUILayout.Slider(
"Vertical FOV",
script.VerticalFov,
SpectatorCameraHelper.VERTICAL_FOV_MIN,
SpectatorCameraHelper.VERTICAL_FOV_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker VerticalFov");
EditorUtility.SetDirty(target);
script.VerticalFov = currentVerticalFov;
}
#endregion
EditorGUILayout.LabelField("\n");
#region Setting related to panorama capturing of spectator camera
// Panorama resolution
EditorGUILayout.LabelField("<b>[ Panorama Setting ]</b>", labelStyle);
EditorGUILayout.PropertyField(PanoramaResolution, new GUIContent("Resolution"));
// Panorama output format
EditorGUILayout.PropertyField(PanoramaOutputFormat, new GUIContent("Output Format"));
// Panorama output type
EditorGUILayout.PropertyField(PanoramaOutputType, new GUIContent("Output Type"));
#endregion
EditorGUILayout.LabelField("\n");
#region Setting related to frustum
if (script.IsFrustumShowed)
{
EditorGUILayout.LabelField("<b>[ Frustum Setting ]</b>", labelStyle);
EditorGUI.BeginChangeCheck();
// Count of frustum line
var currentFrustumLineCount = (SpectatorCameraHelper.FrustumLineCount)
EditorGUILayout.EnumPopup("Frustum Line Total", script.FrustumLineCount);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker FrustumLineCount");
EditorUtility.SetDirty(target);
script.FrustumLineCount = currentFrustumLineCount;
}
EditorGUI.BeginChangeCheck();
// Count of frustum center line
var currentFrustumCenterLineCount = (SpectatorCameraHelper.FrustumCenterLineCount)
EditorGUILayout.EnumPopup("Frustum Center Line Total", script.FrustumCenterLineCount);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker FrustumCenterLineCount");
EditorUtility.SetDirty(target);
script.FrustumCenterLineCount = currentFrustumCenterLineCount;
}
EditorGUILayout.LabelField("\n");
EditorGUI.BeginChangeCheck();
// Width of frustum line
var currentFrustumLineWidth =
EditorGUILayout.Slider(
"Frustum Line Width",
script.FrustumLineWidth,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker FrustumLineWidth");
EditorUtility.SetDirty(target);
script.FrustumLineWidth = currentFrustumLineWidth;
}
EditorGUI.BeginChangeCheck();
// Width of frustum center line
var currentFrustumCenterLineWidth =
EditorGUILayout.Slider(
"Frustum Center Line Width",
script.FrustumCenterLineWidth,
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MAX);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker FrustumCenterLineWidth");
EditorUtility.SetDirty(target);
script.FrustumCenterLineWidth = currentFrustumCenterLineWidth;
}
EditorGUILayout.LabelField("\n");
EditorGUI.BeginChangeCheck();
// Color of frustum line
var currentFrustumLineColor = EditorGUILayout.ColorField(
"Frustum Line Color", script.FrustumLineColor);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker FrustumLineColor");
EditorUtility.SetDirty(target);
script.FrustumLineColor = currentFrustumLineColor;
}
EditorGUI.BeginChangeCheck();
// Color of frustum center line
var currentFrustumCenterLineColor = EditorGUILayout.ColorField(
"Frustum Center Line Color",
script.FrustumCenterLineColor);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, $"Modified {target.name} SpectatorCameraTracker FrustumCenterLineColor");
EditorUtility.SetDirty(target);
script.FrustumCenterLineColor = currentFrustumCenterLineColor;
}
EditorGUILayout.LabelField("\n");
}
#endregion
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 523f24465def2a246b2f27dca663a530
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,479 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
using VIVE.OpenXR.Toolkits.Spectator.Helper;
namespace VIVE.OpenXR.Toolkits.Spectator
{
/// <summary>
/// Name: SpectatorCameraTracker.cs
/// Role: General script
/// Responsibility: To implement the spectator camera tracker setting and I/O function
/// </summary>
public partial class SpectatorCameraTracker : MonoBehaviour, ISpectatorCameraSetting
{
private const int TryAddTrackerIntervalSecond = 1;
private SpectatorCameraManager SpectatorCameraManager => SpectatorCameraManager.Instance;
public SpectatorCameraHelper.CameraSourceRef CameraSourceRef => SpectatorCameraHelper.CameraSourceRef.Tracker;
public Vector3 Position
{
get => transform.position;
set => transform.position = value;
}
public Quaternion Rotation
{
get => transform.rotation;
set => transform.rotation = value;
}
[SerializeField] private LayerMask layerMask = SpectatorCameraHelper.LayerMaskDefault;
public LayerMask LayerMask
{
get => layerMask;
set
{
if (layerMask == value)
{
return;
}
layerMask = value;
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SpectatorCameraBased.SpectatorCamera.cullingMask = layerMask;
}
}
}
[field: SerializeField]
public bool IsSmoothCameraMovement { get; set; } = SpectatorCameraHelper.IS_SMOOTH_CAMERA_MOVEMENT_DEFAULT;
[field: SerializeField]
public int SmoothCameraMovementSpeed { get; set; } = SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_SPEED_DEFAULT;
[SerializeField] private bool isFrustumShowed = SpectatorCameraHelper.IS_FRUSTUM_SHOWED_DEFAULT;
public bool IsFrustumShowed
{
get => isFrustumShowed;
set
{
if (isFrustumShowed == value)
{
return;
}
isFrustumShowed = value;
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SetupFrustum();
}
}
}
[SerializeField] private float verticalFov = SpectatorCameraHelper.VERTICAL_FOV_DEFAULT;
public float VerticalFov
{
get => verticalFov;
set
{
if (Math.Abs(verticalFov - value) < SpectatorCameraHelper.COMPARE_FLOAT_MEDIUM_THRESHOLD)
{
return;
}
verticalFov = Mathf.Clamp(
value,
SpectatorCameraHelper.VERTICAL_FOV_MIN,
SpectatorCameraHelper.VERTICAL_FOV_MAX);
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SpectatorCameraBased.SpectatorCamera.fieldOfView = verticalFov;
SpectatorCameraManager.SetupFrustum();
}
}
}
#region Panorama properties
[field: SerializeField]
public SpectatorCameraHelper.SpectatorCameraPanoramaResolution PanoramaResolution { get; set; } =
SpectatorCameraHelper.PANORAMA_RESOLUTION_DEFAULT;
[field: SerializeField]
public TextureProcessHelper.PictureOutputFormat PanoramaOutputFormat { get; set; } =
SpectatorCameraHelper.PANORAMA_OUTPUT_FORMAT_DEFAULT;
[field: SerializeField]
public TextureProcessHelper.PanoramaType PanoramaOutputType { get; set; } =
SpectatorCameraHelper.PANORAMA_TYPE_DEFAULT;
#endregion
[SerializeField] private SpectatorCameraHelper.FrustumLineCount frustumLineCount =
SpectatorCameraHelper.FRUSTUM_LINE_COUNT_DEFAULT;
public SpectatorCameraHelper.FrustumLineCount FrustumLineCount
{
get => frustumLineCount;
set
{
if (frustumLineCount == value)
{
return;
}
frustumLineCount = value;
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SetupFrustumLine();
}
}
}
[SerializeField] private SpectatorCameraHelper.FrustumCenterLineCount frustumCenterLineCount =
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_COUNT_DEFAULT;
public SpectatorCameraHelper.FrustumCenterLineCount FrustumCenterLineCount
{
get => frustumCenterLineCount;
set
{
if (frustumCenterLineCount == value)
{
return;
}
frustumCenterLineCount = value;
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SetupFrustumCenterLine();
}
}
}
[SerializeField] private float frustumLineWidth = SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_DEFAULT;
public float FrustumLineWidth
{
get => frustumLineWidth;
set
{
if (Math.Abs(frustumLineWidth - value) < SpectatorCameraHelper.COMPARE_FLOAT_SUPER_SMALL_THRESHOLD)
{
return;
}
frustumLineWidth = Mathf.Clamp(
value,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_MAX);
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SetupFrustumLine();
}
}
}
[SerializeField] private float frustumCenterLineWidth = SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_DEFAULT;
public float FrustumCenterLineWidth
{
get => frustumCenterLineWidth;
set
{
if (Math.Abs(frustumCenterLineWidth - value) <
SpectatorCameraHelper.COMPARE_FLOAT_SUPER_SMALL_THRESHOLD)
{
return;
}
frustumCenterLineWidth = Mathf.Clamp(value, SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MIN,
SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_MAX);
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SetupFrustumCenterLine();
}
}
}
[SerializeField] private Color frustumLineColor = SpectatorCameraHelper.LineColorDefault;
public Color FrustumLineColor
{
get => frustumLineColor;
set
{
if (frustumLineColor == value)
{
return;
}
frustumLineColor = value;
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SetupFrustumLine();
}
}
}
[SerializeField] private Color frustumCenterLineColor = SpectatorCameraHelper.LineColorDefault;
public Color FrustumCenterLineColor
{
get => frustumCenterLineColor;
set
{
if (frustumCenterLineColor == value)
{
return;
}
frustumCenterLineColor = value;
if (Application.isPlaying &&
SpectatorCameraManager != null &&
SpectatorCameraManager.IsCameraSourceAsTracker() &&
SpectatorCameraManager.IsFollowTrackerEqualTo(this))
{
SpectatorCameraManager.SetupFrustumCenterLine();
}
}
}
public void ResetSetting()
{
LayerMask = SpectatorCameraHelper.LayerMaskDefault;
IsSmoothCameraMovement = SpectatorCameraHelper.IS_SMOOTH_CAMERA_MOVEMENT_DEFAULT;
SmoothCameraMovementSpeed = SpectatorCameraHelper.SMOOTH_CAMERA_MOVEMENT_SPEED_DEFAULT;
IsFrustumShowed = SpectatorCameraHelper.IS_FRUSTUM_SHOWED_DEFAULT;
VerticalFov = SpectatorCameraHelper.VERTICAL_FOV_DEFAULT;
PanoramaResolution = SpectatorCameraHelper.PANORAMA_RESOLUTION_DEFAULT;
PanoramaOutputFormat = SpectatorCameraHelper.PANORAMA_OUTPUT_FORMAT_DEFAULT;
PanoramaOutputType = SpectatorCameraHelper.PANORAMA_TYPE_DEFAULT;
FrustumLineCount = SpectatorCameraHelper.FRUSTUM_LINE_COUNT_DEFAULT;
FrustumCenterLineCount = SpectatorCameraHelper.FRUSTUM_CENTER_LINE_COUNT_DEFAULT;
FrustumLineWidth = SpectatorCameraHelper.FRUSTUM_LINE_WIDTH_DEFAULT;
FrustumCenterLineWidth = SpectatorCameraHelper.FRUSTUM_CENTER_LINE_WIDTH_DEFAULT;
FrustumLineColor = SpectatorCameraHelper.LineColorDefault;
FrustumCenterLineColor = SpectatorCameraHelper.LineColorDefault;
}
public void ExportSetting2JsonFile(in SpectatorCameraHelper.AttributeFileLocation attributeFileLocation)
{
#if !UNITY_EDITOR
if (attributeFileLocation is SpectatorCameraHelper.AttributeFileLocation.ResourceFolder)
{
Debug.LogError("It's not allowed to save setting to resource folder in runtime mode");
return;
}
#endif
var data = new SpectatorCameraHelper.SpectatorCameraAttribute(
CameraSourceRef,
Position,
Rotation,
LayerMask,
IsSmoothCameraMovement,
SmoothCameraMovementSpeed,
IsFrustumShowed,
VerticalFov,
PanoramaResolution,
PanoramaOutputFormat,
PanoramaOutputType,
FrustumLineCount,
FrustumCenterLineCount,
FrustumLineWidth,
FrustumCenterLineWidth,
FrustumLineColor,
FrustumCenterLineColor);
#if UNITY_EDITOR
if (attributeFileLocation is SpectatorCameraHelper.AttributeFileLocation.ResourceFolder)
{
SpectatorCameraHelper.SaveAttributeData2ResourcesFolder(
SceneManager.GetActiveScene().name,
gameObject.name,
data);
}
else if (attributeFileLocation is SpectatorCameraHelper.AttributeFileLocation.PersistentFolder)
{
SpectatorCameraHelper.SaveAttributeData2PersistentFolder(
SceneManager.GetActiveScene().name,
gameObject.name,
data);
}
#else
SpectatorCameraHelper.SaveAttributeData2PersistentFolder(
SceneManager.GetActiveScene().name,
gameObject.name,
data);
#endif
}
public void LoadSettingFromJsonFile(in string jsonFilePath)
{
bool loadSuccess = SpectatorCameraHelper.LoadAttributeFileFromFolder(
jsonFilePath,
out SpectatorCameraHelper.SpectatorCameraAttribute data);
if (loadSuccess)
{
ApplyData(data);
}
else
{
Debug.Log($"Load setting from {jsonFilePath} file to scene gameObject {gameObject.name} failed.");
}
}
public void LoadSettingFromJsonFile(
in string sceneName,
in string trackerName,
in SpectatorCameraHelper.AttributeFileLocation attributeFileLocation)
{
if (string.IsNullOrEmpty(sceneName) || string.IsNullOrEmpty(trackerName))
{
Debug.LogError("sceneName or trackerName is null or empty");
return;
}
var loadSuccess = false;
SpectatorCameraHelper.SpectatorCameraAttribute data = new SpectatorCameraHelper.SpectatorCameraAttribute();
if (attributeFileLocation is SpectatorCameraHelper.AttributeFileLocation.ResourceFolder)
{
loadSuccess = SpectatorCameraHelper.LoadAttributeFileFromResourcesFolder(
sceneName,
trackerName,
out data);
}
else if (attributeFileLocation is SpectatorCameraHelper.AttributeFileLocation.PersistentFolder)
{
loadSuccess = SpectatorCameraHelper.LoadAttributeFileFromPersistentFolder(
sceneName,
trackerName,
out data);
}
if (loadSuccess)
{
ApplyData(data);
}
else
{
var fileDirectory = string.Empty;
if (attributeFileLocation is SpectatorCameraHelper.AttributeFileLocation.ResourceFolder)
{
fileDirectory = System.IO.Path.Combine(Application.dataPath, "Resources");
}
else if (attributeFileLocation is SpectatorCameraHelper.AttributeFileLocation.PersistentFolder)
{
fileDirectory = Application.persistentDataPath;
}
var fileName =
SpectatorCameraHelper.GetSpectatorCameraAttributeFileNamePattern(sceneName, trackerName);
Debug.Log(
$"Load setting from {fileDirectory}/{fileName} file to scene gameObject {gameObject.name} failed.");
}
}
public void ApplyData(in SpectatorCameraHelper.SpectatorCameraAttribute data)
{
Transform gameObjectTransform = transform;
gameObjectTransform.position = data.position;
gameObjectTransform.rotation = data.rotation;
LayerMask = data.layerMask;
IsSmoothCameraMovement = data.isSmoothCameraMovement;
SmoothCameraMovementSpeed = data.smoothCameraMovementSpeed;
IsFrustumShowed = data.isFrustumShowed;
VerticalFov = data.verticalFov;
PanoramaResolution = data.panoramaResolution;
PanoramaOutputFormat = data.panoramaOutputFormat;
PanoramaOutputType = data.panoramaOutputType;
FrustumLineCount = data.frustumLineCount;
FrustumCenterLineCount = data.frustumCenterLineCount;
FrustumLineWidth = data.frustumLineWidth;
FrustumCenterLineWidth = data.frustumCenterLineWidth;
FrustumLineColor = data.frustumLineColor;
FrustumCenterLineColor = data.frustumCenterLineColor;
}
private void Start()
{
if (SpectatorCameraManager != null)
{
SpectatorCameraManager.AddSpectatorCameraTracker(this);
}
else
{
InvokeRepeating(nameof(AddTracker), TryAddTrackerIntervalSecond, TryAddTrackerIntervalSecond);
}
}
private void OnEnable()
{
if (SpectatorCameraManager != null)
{
SpectatorCameraManager.AddSpectatorCameraTracker(this);
}
}
private void OnDisable()
{
if (SpectatorCameraManager != null)
{
SpectatorCameraManager.RemoveSpectatorCameraTracker(this);
}
}
private void AddTracker()
{
if (SpectatorCameraManager != null)
{
SpectatorCameraManager.AddSpectatorCameraTracker(this);
CancelInvoke(nameof(AddTracker));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0b61457a6a405c49b000f7a3ed895bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: