version 2.5.0
This commit is contained in:
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52cbc1ad9d288d348854b4e81d8aa57c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2fb164a4bcc8f04e9337299f4c030d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d28163e4ec0b605478774041d1222262
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05c260c7d911cff49b0333f5ba628420
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3136b6864f4cb9c4da5234953d3a75b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09984648d0f364648af4f0ac5a03de55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user