diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d0d1b1d278..0976141387 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,6 +17,7 @@ env:
UNITY_EMAIL: ${{ fromJSON(format('["unitytest@mikeage.net", "{0}"]', secrets.UNITY_EMAIL))[secrets.UNITY_SERIAL != null] }}
UNITY_PASSWORD: ${{ fromJSON(format('["DoNotStealThis1", "{0}"]', secrets.UNITY_PASSWORD))[secrets.UNITY_SERIAL != null] }}
UNITY_LICENSE: ${{ fromJSON('["\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 9DZF11miRAzx7TIuCxih78B6CXU=weRQubqMNN61lSZtm/e7S+UDzTPNQjM5aQl/c4aKLH/b2khefpgLfdWneoxdnNopA6+rW6kBqxWt\nhMLdHY+oAOfDsfmMQRTnmQG0Y3G3xh6gjGP1RAIHoLDfFHf+0LQ3FakA2WehcFWPSYeVDrdxm3HW\nqMmdKWooD9i+J4s4rQFTDx9+/G6yjc5KGTyGxIz3c5kxTEkV2qsFPXsauomY9Z8YPKy+cZK7g+Ol\npO+LhtzetgTIlIN/qG8eByjlp6nOuVGdDOIrhNJW+vllNyx0qNWPREadVrhFViI4UXegMFRl5gJc\nrgcrlr/fD+NorDVLfcu7D863QXkkuriILUIq2Q==", null]')[secrets.UNITY_SERIAL != null] }}
+ PHOTON_PAT: ${{ secrets.PHOTON_PAT }}
jobs:
configuration:
if: |
@@ -273,6 +274,8 @@ jobs:
mkdir Packages/com.unity.xr.picoxr
wget -q https://sdk.picovr.com/developer-platform/sdk/PICO%20Unity%20Integration%20SDK%20v211.zip -O package.zip
unzip package.zip -d Packages/com.unity.xr.picoxr
+ # Pico has a GUID conflict because they copied code from Oculus. Delete the offending .meta file from our mutable Pico SDK.
+ rm Packages/com.unity.xr.picoxr/Platform/Scripts/Models/Common.cs.meta
- name: Install TextMesh Pro package
run: |
@@ -296,6 +299,21 @@ jobs:
cp -R 'tmp.package/Assets/TextMesh Pro' Assets/
rm -rf tmp.plugin tmp.package
+ - name: Checkout private photon repository if available
+ if: ${{ env.PHOTON_PAT }}
+ uses: actions/checkout@v4
+ with:
+ token: ${{ env.PHOTON_PAT }}
+ repository: icosa-mirror/photon-fusion
+ path: photon-fusion-mirror/
+
+ - name: Copy photon files
+ if: ${{ env.PHOTON_PAT }}
+ run: |
+ rsync -a --ignore-existing photon-fusion-mirror/ ./
+ echo "For debugging: these are the files in Assets/Photon:"
+ find Assets/Photon
+
- name: Restore Library/
id: cache_library
uses: actions/cache/restore@v3
diff --git a/.gitignore b/.gitignore
index b85fc81855..48368ea94c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,5 +102,10 @@
# Pico Integration
Packages/com.unity.xr.picoxr
+# Photon Integration
+/Assets/Photon/**
+/Assets/Photon.meta
+!/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion*
+
# Cache files
*.cache
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3a10c68900..aa7780d22e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -31,7 +31,7 @@ repos:
rev: "a2c1431dabf2229052cf598f86f31bc3eac5bc7b" # Main as of 2022-12-05
hooks:
- id: dotnet-format
- exclude: ^(Assets/ThirdParty)|(Packages/)
+ exclude: ^(Assets/ThirdParty)|(Packages/)|(Assets/Photon/)
entry: dotnet format whitespace
args:
- --folder
diff --git a/Assets/Editor/BuildTiltBrushPostProcess.cs b/Assets/Editor/BuildTiltBrushPostProcess.cs
new file mode 100644
index 0000000000..328a112bfb
--- /dev/null
+++ b/Assets/Editor/BuildTiltBrushPostProcess.cs
@@ -0,0 +1,113 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.IO;
+using System.Xml;
+using UnityEditor;
+#if UNITY_ANDROID
+using UnityEditor.Android;
+#endif
+
+[InitializeOnLoad]
+public class BuildTiltBrushPostProcess
+#if UNITY_ANDROID
+ : IPostGenerateGradleAndroidProject
+#endif
+{
+ // OVRGradleGeneration is 99999, so we'll just go to the extreme.
+ public int callbackOrder => 1000000;
+
+ public void OnPostGenerateGradleAndroidProject(string path)
+ {
+ string manifestFolder = Path.Combine(path, "src/main");
+ string file = manifestFolder + "/AndroidManifest.xml";
+
+ try
+ {
+ XmlDocument doc = new XmlDocument();
+ doc.Load(file);
+
+ XmlElement element = (XmlElement)doc.SelectSingleNode("/manifest");
+ var androidNamespaceURI = element.GetAttribute("xmlns:android");
+
+
+
+ UnityEngine.Debug.Log("Add all quest supported devices.");
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application",
+ "meta-data",
+ "com.oculus.supportedDevices",
+ true,
+ true,
+ "value", "quest|quest2"
+ );
+
+ doc.Save(file);
+ }
+ catch (System.Exception e)
+ {
+ UnityEngine.Debug.LogException(e);
+ }
+ }
+
+ private static void AddOrRemoveTag(XmlDocument doc, string @namespace, string path, string elementName, string name,
+ bool required, bool modifyIfFound, params string[] attrs) // name, value pairs
+ {
+ var nodes = doc.SelectNodes(path + "/" + elementName);
+ XmlElement element = null;
+ foreach (XmlElement e in nodes)
+ {
+ if (name == null || name == e.GetAttribute("name", @namespace))
+ {
+ element = e;
+ break;
+ }
+ }
+
+ if (required)
+ {
+ if (element == null)
+ {
+ var parent = doc.SelectSingleNode(path);
+ element = doc.CreateElement(elementName);
+ element.SetAttribute("name", @namespace, name);
+ parent.AppendChild(element);
+ }
+
+ for (int i = 0; i < attrs.Length; i += 2)
+ {
+ if (modifyIfFound || string.IsNullOrEmpty(element.GetAttribute(attrs[i], @namespace)))
+ {
+ if (attrs[i + 1] != null)
+ {
+ element.SetAttribute(attrs[i], @namespace, attrs[i + 1]);
+ }
+ else
+ {
+ element.RemoveAttribute(attrs[i], @namespace);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (element != null && modifyIfFound)
+ {
+ element.ParentNode.RemoveChild(element);
+ }
+ }
+ }
+
+}
diff --git a/Assets/Editor/BuildTiltBrushPostProcess.cs.meta b/Assets/Editor/BuildTiltBrushPostProcess.cs.meta
new file mode 100644
index 0000000000..e131169336
--- /dev/null
+++ b/Assets/Editor/BuildTiltBrushPostProcess.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 151adde7344d1624cb94167188535adc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Oculus/OculusProjectConfig.asset b/Assets/Oculus/OculusProjectConfig.asset
index cca3bd5d40..4280c0f504 100644
--- a/Assets/Oculus/OculusProjectConfig.asset
+++ b/Assets/Oculus/OculusProjectConfig.asset
@@ -17,13 +17,16 @@ MonoBehaviour:
handTrackingSupport: 0
handTrackingFrequency: 0
handTrackingVersion: 0
- anchorSupport: 0
- sharedAnchorSupport: 0
+ multimodalHandsControllersSupport: 0
+ anchorSupport: 1
+ sharedAnchorSupport: 1
renderModelSupport: 0
trackedKeyboardSupport: 0
bodyTrackingSupport: 0
faceTrackingSupport: 0
eyeTrackingSupport: 0
+ virtualKeyboardSupport: 0
+ sceneSupport: 1
disableBackups: 0
enableNSCConfig: 0
securityXmlPath:
@@ -31,7 +34,9 @@ MonoBehaviour:
focusAware: 1
requiresSystemKeyboard: 0
experimentalFeaturesEnabled: 0
- insightPassthroughEnabled: 1
+ insightPassthroughEnabled: 0
+ _insightPassthroughSupport: 1
systemSplashScreen: {fileID: 0}
- ovrPluginMd5Win64: cc745177e7550713731157d64af22834
- ovrPluginMd5Android: 997ec6c34867e09e006518cbedd3a649
+ systemSplashScreenType: 0
+ ovrPluginMd5Win64: 67e58deb5b905ae742512bbee3e06a70
+ ovrPluginMd5Android: 017d859ada160e45b27588f910d5d8d8
diff --git a/Assets/OculusMR.meta b/Assets/OculusMR.meta
new file mode 100644
index 0000000000..d53462ab23
--- /dev/null
+++ b/Assets/OculusMR.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 180a5bc23d784424188dd3d6c5534cd4
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/OculusMR/AnchorToGuide.cs b/Assets/OculusMR/AnchorToGuide.cs
new file mode 100644
index 0000000000..3d0225f9c1
--- /dev/null
+++ b/Assets/OculusMR/AnchorToGuide.cs
@@ -0,0 +1,74 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace TiltBrush
+{
+ public class AnchorToGuide : MonoBehaviour
+ {
+ // Start is called before the first frame update
+
+ private OVRSceneVolume m_SceneComponentVolume;
+ private OVRScenePlane m_SceneComponentPlane;
+ private OVRSemanticClassification m_Classification;
+
+ void Start()
+ {
+ m_SceneComponentVolume = GetComponent();
+
+ if (m_SceneComponentVolume)
+ {
+ var dimentions = m_SceneComponentVolume.Dimensions;
+
+ var pos = App.Scene.transform.InverseTransformPoint(this.transform.position);
+ pos.y /= 2.0f;
+ var tr = TrTransform.TR(pos, this.transform.rotation);
+
+ CreateWidgetCommand createCommand = new CreateWidgetCommand(
+ WidgetManager.m_Instance.GetStencilPrefab(StencilType.Cube), tr, null, true);
+ SketchMemoryScript.m_Instance.PerformAndRecordCommand(createCommand);
+
+ SketchMemoryScript.m_Instance.PerformAndRecordCommand(
+ new MoveWidgetCommand(createCommand.Widget, tr, dimentions * 10));
+
+ return;
+ }
+
+ // Hacky but quick proof of concept
+ // TODO: Tidyup, seems a bit broken right now.
+ // You end up stuck grabbin the wall planes.
+ // m_SceneComponentPlane = GetComponent();
+
+ // if(m_SceneComponentPlane)
+ // {
+ // var dimentions = m_SceneComponentPlane.Dimensions;
+
+ // var tr = TrTransform.TR(this.transform.position, this.transform.rotation);
+
+ // CreateWidgetCommand createCommand = new CreateWidgetCommand(
+ // WidgetManager.m_Instance.GetStencilPrefab(StencilType.Plane), tr, null, true);
+ // SketchMemoryScript.m_Instance.PerformAndRecordCommand(createCommand);
+
+ // SketchMemoryScript.m_Instance.PerformAndRecordCommand(
+ // new MoveWidgetCommand(createCommand.Widget, tr, dimentions * 10));
+
+ // return;
+ // }
+ }
+ }
+
+}
diff --git a/Assets/OculusMR/AnchorToGuide.cs.meta b/Assets/OculusMR/AnchorToGuide.cs.meta
new file mode 100644
index 0000000000..adff9249de
--- /dev/null
+++ b/Assets/OculusMR/AnchorToGuide.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b2cf8d992af87474ab79d8d4a447a295
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/OculusMR/OculusMR.prefab b/Assets/OculusMR/OculusMR.prefab
new file mode 100644
index 0000000000..011afb48c2
--- /dev/null
+++ b/Assets/OculusMR/OculusMR.prefab
@@ -0,0 +1,82 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &7081236046508280627
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7809753190829035278}
+ - component: {fileID: 5619048699234477578}
+ - component: {fileID: -3473970634249864913}
+ - component: {fileID: 5425681579648101261}
+ m_Layer: 0
+ m_Name: OculusMR
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &7809753190829035278
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7081236046508280627}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &5619048699234477578
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7081236046508280627}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 57acf6fd80bfa99419bc6274bd16c6c8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ ovrSceneManager: {fileID: 5425681579648101261}
+ m_SpatialAnchorManager: {fileID: -3473970634249864913}
+--- !u!114 &-3473970634249864913
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7081236046508280627}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 91e9b9159d619a2428162e46f16afb16, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!114 &5425681579648101261
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7081236046508280627}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f2253d75e3ddc644b8153409c62dd74e, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ PlanePrefab: {fileID: 5531902977439992835, guid: 96517f8af456f7943b86d2b7ed027964,
+ type: 3}
+ VolumePrefab: {fileID: 5531902977439992835, guid: 96517f8af456f7943b86d2b7ed027964,
+ type: 3}
+ PrefabOverrides: []
+ VerboseLogging: 0
+ MaxSceneAnchorUpdatesPerFrame: 3
+ _initialAnchorParent: {fileID: 0}
diff --git a/Assets/OculusMR/OculusMR.prefab.meta b/Assets/OculusMR/OculusMR.prefab.meta
new file mode 100644
index 0000000000..b5dc0c2dc4
--- /dev/null
+++ b/Assets/OculusMR/OculusMR.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d25a8154340103c4883d7fc7ddab0827
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/OculusMR/OculusMRController.cs b/Assets/OculusMR/OculusMRController.cs
new file mode 100644
index 0000000000..e7fc1f847d
--- /dev/null
+++ b/Assets/OculusMR/OculusMRController.cs
@@ -0,0 +1,79 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections;
+using System.Collections.Generic;
+using OpenBrush.Multiplayer;
+using UnityEngine;
+
+namespace TiltBrush
+{
+ public class OculusMRController : MonoBehaviour
+ {
+ public static OculusMRController m_Instance;
+
+ public OVRSceneManager ovrSceneManager;
+ public SpatialAnchorManager m_SpatialAnchorManager;
+
+ private bool loadedScene;
+
+ private bool host;
+
+ void Awake()
+ {
+ m_Instance = this;
+
+ ovrSceneManager = GetComponent();
+ m_SpatialAnchorManager = GetComponent();
+ }
+
+ void RequestScenePermission()
+ {
+ const string permissionString = "com.oculus.permission.USE_SCENE";
+ bool hasUserAuthorizedPermission = UnityEngine.Android.Permission.HasUserAuthorizedPermission(permissionString);
+ if (!hasUserAuthorizedPermission)
+ {
+ UnityEngine.Android.Permission.RequestUserPermission(permissionString);
+ }
+ }
+
+ public async void StartMRExperience(bool isHosting)
+ {
+ host = isHosting;
+
+ if (host)
+ {
+ await m_SpatialAnchorManager.CreateSpatialAnchor();
+ m_SpatialAnchorManager.SceneLocalizeToAnchor();
+ MultiplayerManager.m_Instance.Connect();
+ }
+ else
+ {
+ MultiplayerManager.m_Instance.Connect();
+ }
+ }
+
+ public async void RemoteSyncToAnchor(string uuid)
+ {
+ await m_SpatialAnchorManager.SyncToRemoteAnchor(uuid, OVRSpace.StorageLocation.Cloud);
+
+ if (!loadedScene)
+ {
+ ovrSceneManager.LoadSceneModel();
+ loadedScene = true;
+ }
+ }
+ }
+}
+
diff --git a/Assets/OculusMR/OculusMRController.cs.meta b/Assets/OculusMR/OculusMRController.cs.meta
new file mode 100644
index 0000000000..d7f6fd4b9c
--- /dev/null
+++ b/Assets/OculusMR/OculusMRController.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 57acf6fd80bfa99419bc6274bd16c6c8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/OculusMR/SceneAnchorBase.prefab b/Assets/OculusMR/SceneAnchorBase.prefab
new file mode 100644
index 0000000000..bc9360ed05
--- /dev/null
+++ b/Assets/OculusMR/SceneAnchorBase.prefab
@@ -0,0 +1,59 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1571586382271360071
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3444398428018961088}
+ - component: {fileID: 5531902977439992835}
+ - component: {fileID: 9186225842113814311}
+ m_Layer: 0
+ m_Name: SceneAnchorBase
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &3444398428018961088
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1571586382271360071}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &5531902977439992835
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1571586382271360071}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 8ab3ec25e32ce0c4380714d75e39cb05, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!114 &9186225842113814311
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1571586382271360071}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: b2cf8d992af87474ab79d8d4a447a295, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
diff --git a/Assets/OculusMR/SceneAnchorBase.prefab.meta b/Assets/OculusMR/SceneAnchorBase.prefab.meta
new file mode 100644
index 0000000000..7915d07a04
--- /dev/null
+++ b/Assets/OculusMR/SceneAnchorBase.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 96517f8af456f7943b86d2b7ed027964
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/OculusMR/SpatialAnchorManager.cs b/Assets/OculusMR/SpatialAnchorManager.cs
new file mode 100644
index 0000000000..84a02677a4
--- /dev/null
+++ b/Assets/OculusMR/SpatialAnchorManager.cs
@@ -0,0 +1,189 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace TiltBrush
+{
+ public class SpatialAnchorManager : MonoBehaviour
+ {
+ const string kOriginSpatialAnchorPref = "ORIGIN_SPATIAL_ANCHOR";
+
+ OVRSpatialAnchor m_Anchor;
+
+ public string AnchorUuid
+ {
+ get
+ {
+ if (m_Anchor != null)
+ {
+ return m_Anchor.Uuid.ToString();
+ }
+ return string.Empty;
+ }
+ }
+
+ public async Task CreateSpatialAnchor()
+ {
+ var anchorGO = new GameObject("Origin Anchor");
+ m_Anchor = anchorGO.AddComponent();
+
+ return await SaveAnchor();
+ }
+
+ async Task SaveAnchor()
+ {
+ while (!m_Anchor.Created && !m_Anchor.Localized)
+ {
+ await Task.Yield();
+ }
+
+ //Local save, then cloud save.
+ var success = await m_Anchor.SaveAsync();
+
+ if (!success)
+ {
+ return false;
+ }
+
+ Debug.Log("Anchor saved to device!");
+ PlayerPrefs.SetString(kOriginSpatialAnchorPref, m_Anchor.Uuid.ToString());
+
+ success = await m_Anchor.SaveAsync(saveOptions: new OVRSpatialAnchor.SaveOptions { Storage = OVRSpace.StorageLocation.Cloud });
+ if (!success)
+ {
+ return false;
+ }
+ Debug.Log("Anchor saved to cloud!");
+
+ return true;
+ }
+
+ public async Task LoadSpatialAnchor()
+ {
+ if (PlayerPrefs.HasKey(kOriginSpatialAnchorPref))
+ {
+ var guidString = PlayerPrefs.GetString(kOriginSpatialAnchorPref);
+ var guid = new Guid(guidString);
+
+ var data = await OVRSpatialAnchor.LoadUnboundAnchorsAsync(new OVRSpatialAnchor.LoadOptions()
+ {
+ StorageLocation = OVRSpace.StorageLocation.Local,
+ Timeout = 0,
+ Uuids = new List() { guid }
+ }
+ );
+
+ return await BindAnchors(data);
+ }
+ else
+ {
+ return await CreateSpatialAnchor();
+ }
+ }
+
+ public bool SceneLocalizeToAnchor()
+ {
+ if (m_Anchor == null)
+ {
+ return false;
+ }
+
+ var m_anchorTr = TrTransform.FromTransform(m_Anchor.transform);
+
+ var newPose = SketchControlsScript.MakeValidScenePose(m_anchorTr,
+ SceneSettings.m_Instance.HardBoundsRadiusMeters_SS);
+ App.Scene.Pose = newPose;
+
+ Debug.Log("Anchor localized!");
+ return true;
+ }
+
+ public async Task SyncToRemoteAnchor(string uuid, OVRSpace.StorageLocation defaultStorageLocation = OVRSpace.StorageLocation.Local)
+ {
+ var guid = new Guid(uuid);
+
+ var data = await OVRSpatialAnchor.LoadUnboundAnchorsAsync(new OVRSpatialAnchor.LoadOptions()
+ {
+ StorageLocation = defaultStorageLocation,
+ Timeout = 0,
+ Uuids = new List() { guid }
+ }
+ );
+
+ Debug.Log("Remote anchor recieved!");
+ bool bindSuccess = await BindAnchors(data);
+
+ if (bindSuccess)
+ {
+ Debug.Log("Remote anchor bound!");
+ return SceneLocalizeToAnchor();
+ }
+ return false;
+ }
+
+ async Task BindAnchors(OVRSpatialAnchor.UnboundAnchor[] anchors)
+ {
+ var unboundAnchor = anchors[0];
+
+ var anchorGO = new GameObject("Origin Anchor");
+ m_Anchor = anchorGO.AddComponent();
+ unboundAnchor.BindTo(m_Anchor);
+
+ while (m_Anchor.PendingCreation)
+ {
+ await Task.Yield();
+ }
+
+ Debug.Log("Cached anchor created!");
+
+ while (!m_Anchor.Localized)
+ {
+ await Task.Yield();
+ }
+
+ return true;
+ }
+
+ public async Task ShareAnchors(List playerIds)
+ {
+ if (m_Anchor == null)
+ {
+ return false;
+ }
+
+ var spaceUserList = new List();
+ foreach (var id in playerIds)
+ {
+ Debug.Log($"new share id: {id}");
+ spaceUserList.Add(new OVRSpaceUser(id));
+ }
+
+ // TODO: Check anchor exists and is in cloud storage.
+ var result = await m_Anchor.ShareAsync(spaceUserList);
+
+ if (result == OVRSpatialAnchor.OperationResult.Success)
+ {
+ Debug.Log($"Share complete!");
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Assets/OculusMR/SpatialAnchorManager.cs.meta b/Assets/OculusMR/SpatialAnchorManager.cs.meta
new file mode 100644
index 0000000000..86324176de
--- /dev/null
+++ b/Assets/OculusMR/SpatialAnchorManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 91e9b9159d619a2428162e46f16afb16
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion
new file mode 100644
index 0000000000..526a559a17
--- /dev/null
+++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion
@@ -0,0 +1,164 @@
+{
+ "Version": 1,
+ "TypeId": "NetworkProjectConfig",
+ "PeerMode": 0,
+ "PhysicsEngine": 0,
+ "ServerPhysicsMode": 0,
+ "UseLagCompensation": true,
+ "LagCompensation": {
+ "HitboxBufferSize": 12,
+ "HitboxCapacity": 512,
+ "ExpansionFactor": 0.20000000298023225,
+ "Optimize": false,
+ "DebugBroadphase": false,
+ "DebugHistory": false,
+ "DebugColor": {
+ "r": 0.0,
+ "g": 1.0,
+ "b": 0.0,
+ "a": 0.5
+ },
+ "ClientDebugColor": {
+ "r": 0.0,
+ "g": 0.0,
+ "b": 1.0,
+ "a": 0.5
+ },
+ "HistoryDebugColor": {
+ "r": 0.0,
+ "g": 0.0,
+ "b": 1.0,
+ "a": 0.5
+ }
+ },
+ "SceneLoadSpawnMode": 0,
+ "DeltaCompressor": 0,
+ "InvokeRenderInBatchMode": true,
+ "MaxNetworkedObjectCount": 8192,
+ "NetworkIdIsObjectName": false,
+ "HideNetworkObjectInactivityGuard": false,
+ "EnableHostMigration": false,
+ "HostMigrationSnapshotInterval": 60,
+ "Simulation": {
+ "InputDataWordCount": 0,
+ "TickRate": 60,
+ "MaxPrediction": 60,
+ "DefaultPlayers": 10,
+ "ReplicationMode": 0,
+ "ObjectInterest": false,
+ "ServerPacketInterval": 1,
+ "ClientPacketInterval": 1
+ },
+ "Interpolation": {
+ "DeltaAdjustment": 1,
+ "AllowedJitter": 25,
+ "SnapLimit": 200,
+ "MultiplierMin": 1.25,
+ "MultiplierMax": 3.0
+ },
+ "Network": {
+ "SocketSendBufferSize": 256,
+ "SocketRecvBufferSize": 256,
+ "ConnectAttempts": 10,
+ "ConnectInterval": 0.5,
+ "ConnectionDefaultRtt": 0.1,
+ "ConnectionTimeout": 10.0,
+ "ConnectionPingInterval": 1.0,
+ "ConnectionShutdownTime": 1.0,
+ "MtuDefault": 1136,
+ "ReliableDataTransferModes": 3
+ },
+ "NetworkConditions": {
+ "Enabled": false,
+ "DelayShape": 1,
+ "DelayMin": 0.01,
+ "DelayMax": 0.1,
+ "DelayPeriod": 3.0,
+ "DelayThreshold": 0.5,
+ "AdditionalJitter": 0.01,
+ "LossChanceShape": 1,
+ "LossChanceMin": 0.0,
+ "LossChanceMax": 0.02,
+ "LossChanceThreshold": 0.9,
+ "LossChancePeriod": 3.0,
+ "AdditionalLoss": 0.005
+ },
+ "Heap": {
+ "PageShift": 14,
+ "PageCount": 128,
+ "GlobalsSize": 0
+ },
+ "AccuracyDefaults": {
+ "coreKeys": [
+ "Uncompressed",
+ "Default",
+ "Position",
+ "Rotation",
+ "NormalizedTime"
+ ],
+ "coreDefs": [
+ {
+ "_value": 0.0,
+ "_inverse": Infinity,
+ "_hash": -1382104104
+ },
+ {
+ "_value": 0.0010000000474974514,
+ "_inverse": 999.9999389648438,
+ "_hash": -814817977
+ },
+ {
+ "_value": 0.0010000000474974514,
+ "_inverse": 999.9999389648438,
+ "_hash": -194233199
+ },
+ {
+ "_value": 0.0010000000474974514,
+ "_inverse": 999.9999389648438,
+ "_hash": -258202764
+ },
+ {
+ "_value": 0.00009999999747378752,
+ "_inverse": 10000.0,
+ "_hash": 1061325578
+ }
+ ],
+ "coreVals": [
+ {
+ "_value": 0.0,
+ "_inverse": Infinity,
+ "_hash": -1382104104
+ },
+ {
+ "_value": 0.0010000000474974514,
+ "_inverse": 999.9999389648438,
+ "_hash": -814817977
+ },
+ {
+ "_value": 0.0010000000474974514,
+ "_inverse": 999.9999389648438,
+ "_hash": -194233199
+ },
+ {
+ "_value": 0.0010000000474974514,
+ "_inverse": 999.9999389648438,
+ "_hash": -258202764
+ },
+ {
+ "_value": 0.00009999999747378752,
+ "_inverse": 10000.0,
+ "_hash": 1061325578
+ }
+ ],
+ "tags": [],
+ "values": []
+ },
+ "AssembliesToWeave": [
+ "Assembly-CSharp",
+ "Assembly-CSharp-firstpass"
+ ],
+ "UseSerializableDictionary": true,
+ "NullChecksForNetworkedProperties": true,
+ "CheckRpcAttributeUsage": false,
+ "CheckNetworkedPropertiesBeingEmpty": false
+}
\ No newline at end of file
diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta
new file mode 100644
index 0000000000..4e2a77bdf0
--- /dev/null
+++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dd4ca0370d231e84e889cd4edcaf0bde
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 11500000, guid: 66a64a17d0b40f34f9224317a5a84bf2, type: 3}
+ PrefabAssetsContainerPath:
diff --git a/Assets/Plugins/Android/AndroidManifest.xml b/Assets/Plugins/Android/AndroidManifest.xml
index 8b803d7168..93f8cb04f6 100644
--- a/Assets/Plugins/Android/AndroidManifest.xml
+++ b/Assets/Plugins/Android/AndroidManifest.xml
@@ -19,4 +19,5 @@
+
diff --git a/Assets/Prefabs/HMDMesh.prefab b/Assets/Prefabs/HMDMesh.prefab
new file mode 100644
index 0000000000..6656788973
--- /dev/null
+++ b/Assets/Prefabs/HMDMesh.prefab
@@ -0,0 +1,85 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1448111865499013753
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1448111865499013754}
+ - component: {fileID: 1448111865499013748}
+ - component: {fileID: 1448111865499013755}
+ m_Layer: 15
+ m_Name: HMDMesh
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1448111865499013754
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1448111865499013753}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &1448111865499013748
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1448111865499013753}
+ m_Mesh: {fileID: 4300000, guid: dd4ebe901b35bd949a7116ab582586b9, type: 3}
+--- !u!23 &1448111865499013755
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1448111865499013753}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 0
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 981fe2686b81af74ca426ec05bdfe481, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 1
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
diff --git a/Assets/Prefabs/HMDMesh.prefab.meta b/Assets/Prefabs/HMDMesh.prefab.meta
new file mode 100644
index 0000000000..e7f82c1e63
--- /dev/null
+++ b/Assets/Prefabs/HMDMesh.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 834f4ac71ec35d148acacfca28f04405
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Multiplayer.meta b/Assets/Resources/Multiplayer.meta
new file mode 100644
index 0000000000..5797da00ec
--- /dev/null
+++ b/Assets/Resources/Multiplayer.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 13faaeff6bdbe034ca7a725fd0ce5771
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Multiplayer/Photon.meta b/Assets/Resources/Multiplayer/Photon.meta
new file mode 100644
index 0000000000..49cbffbb03
--- /dev/null
+++ b/Assets/Resources/Multiplayer/Photon.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2b0e9afea1d90794ab08a7729ba8d55c
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab
new file mode 100644
index 0000000000..524ebbe795
--- /dev/null
+++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab
@@ -0,0 +1,704 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &303516855785969295
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6718544642020818535}
+ - component: {fileID: 7533779467632377196}
+ - component: {fileID: 6752862290110515822}
+ m_Layer: 0
+ m_Name: PhotonPlayerRig
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &6718544642020818535
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 303516855785969295}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 5041384770245166357}
+ - {fileID: 1457871284126546349}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7533779467632377196
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 303516855785969295}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f250b0d6a1df335439813ad43ce3dbdb, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _interpolationDataSource: 0
+ m_PlayArea: {fileID: 8895232112724846442}
+ m_PlayerHead: {fileID: 5083753190464579125}
+ m_Left: {fileID: 6268094594865570998}
+ m_Right: {fileID: 4999079164026519325}
+ m_Tool: {fileID: 5771162771103583132}
+ headTransform: {fileID: 5041384770245166357}
+ _oculusPlayerId: 0
+--- !u!114 &6752862290110515822
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 303516855785969295}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ ObjectInterest: 1
+ DefaultInterestGroups: []
+ DestroyWhenStateAuthorityLeaves: 0
+ AllowStateAuthorityOverride: 0
+ AoiPositionSource: {fileID: 0}
+ Flags: 2305
+ NetworkGuid:
+ RawGuidValue: 9ee52735aebb6a445b4905c8aba8cfe3
+ NestedObjects: []
+ NetworkedBehaviours:
+ - {fileID: 7533779467632377196}
+ - {fileID: 8895232112724846442}
+ - {fileID: 5083753190464579125}
+ - {fileID: 6268094594865570998}
+ - {fileID: 4999079164026519325}
+ - {fileID: 5771162771103583132}
+ SimulationBehaviours: []
+--- !u!1 &459460118330075700
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6539275587195953435}
+ - component: {fileID: 8895232112724846442}
+ m_Layer: 0
+ m_Name: Area
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &6539275587195953435
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 459460118330075700}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 3005319065403690882}
+ m_Father: {fileID: 1457871284126546349}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8895232112724846442
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 459460118330075700}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _interpolationDataSource: 0
+ InterpolationSpace: 0
+ InterpolationTarget: {fileID: 3005319065403690882}
+ InterpolateErrorCorrection: 1
+ InterpolatedErrorCorrectionSettings:
+ MinRate: 3.3
+ MaxRate: 10
+ PosBlendStart: 0.25
+ PosBlendEnd: 1
+ PosMinCorrection: 0.025
+ PosTeleportDistance: 2
+ RotBlendStart: 0.1
+ RotBlendEnd: 0.5
+ RotTeleportRadians: 1.5
+ UseLegacySharedModeInterpolation: 0
+ TargetInterpolationDelay: 0.03
+--- !u!1 &1184365666732702344
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7668165333132264083}
+ - component: {fileID: 4999079164026519325}
+ m_Layer: 0
+ m_Name: RightHand
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &7668165333132264083
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1184365666732702344}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 4501806461527917157}
+ m_Father: {fileID: 1457871284126546349}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4999079164026519325
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1184365666732702344}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _interpolationDataSource: 0
+ InterpolationSpace: 0
+ InterpolationTarget: {fileID: 4501806461527917157}
+ InterpolateErrorCorrection: 1
+ InterpolatedErrorCorrectionSettings:
+ MinRate: 3.3
+ MaxRate: 10
+ PosBlendStart: 0.25
+ PosBlendEnd: 1
+ PosMinCorrection: 0.025
+ PosTeleportDistance: 2
+ RotBlendStart: 0.1
+ RotBlendEnd: 0.5
+ RotTeleportRadians: 1.5
+ UseLegacySharedModeInterpolation: 0
+ TargetInterpolationDelay: 0.03
+--- !u!1 &2181518286534520838
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2439271445520760248}
+ m_Layer: 0
+ m_Name: DummyTool
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2439271445520760248
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2181518286534520838}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8617970912018780043}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &2546328019691961597
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1030654070696327489}
+ - component: {fileID: 5083753190464579125}
+ m_Layer: 0
+ m_Name: Head
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1030654070696327489
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2546328019691961597}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 2514485848220458462}
+ m_Father: {fileID: 1457871284126546349}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &5083753190464579125
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2546328019691961597}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _interpolationDataSource: 0
+ InterpolationSpace: 0
+ InterpolationTarget: {fileID: 2514485848220458462}
+ InterpolateErrorCorrection: 1
+ InterpolatedErrorCorrectionSettings:
+ MinRate: 3.3
+ MaxRate: 10
+ PosBlendStart: 0.25
+ PosBlendEnd: 1
+ PosMinCorrection: 0.025
+ PosTeleportDistance: 2
+ RotBlendStart: 0.1
+ RotBlendEnd: 0.5
+ RotTeleportRadians: 1.5
+ UseLegacySharedModeInterpolation: 0
+ TargetInterpolationDelay: 0.03
+--- !u!1 &2568790808486584579
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5887326793658353859}
+ - component: {fileID: 6268094594865570998}
+ m_Layer: 0
+ m_Name: LeftHand
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &5887326793658353859
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2568790808486584579}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1310333546402417676}
+ m_Father: {fileID: 1457871284126546349}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &6268094594865570998
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2568790808486584579}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _interpolationDataSource: 0
+ InterpolationSpace: 0
+ InterpolationTarget: {fileID: 1310333546402417676}
+ InterpolateErrorCorrection: 1
+ InterpolatedErrorCorrectionSettings:
+ MinRate: 3.3
+ MaxRate: 10
+ PosBlendStart: 0.25
+ PosBlendEnd: 1
+ PosMinCorrection: 0.025
+ PosTeleportDistance: 2
+ RotBlendStart: 0.1
+ RotBlendEnd: 0.5
+ RotTeleportRadians: 1.5
+ UseLegacySharedModeInterpolation: 0
+ TargetInterpolationDelay: 0.03
+--- !u!1 &3333659737751141766
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3005319065403690882}
+ m_Layer: 0
+ m_Name: DummyArea
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &3005319065403690882
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3333659737751141766}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 6539275587195953435}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5681254972916902966
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5041384770245166357}
+ m_Layer: 0
+ m_Name: HeadTransform
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &5041384770245166357
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5681254972916902966}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 7120707258077581554}
+ m_Father: {fileID: 6718544642020818535}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5736389435991655852
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1310333546402417676}
+ m_Layer: 0
+ m_Name: DummyLeft
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1310333546402417676
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5736389435991655852}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 5887326793658353859}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &6003437268994292288
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4501806461527917157}
+ m_Layer: 0
+ m_Name: DummyRight
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4501806461527917157
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6003437268994292288}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 7668165333132264083}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &7803168780329886017
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8617970912018780043}
+ - component: {fileID: 5771162771103583132}
+ m_Layer: 0
+ m_Name: Tool
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &8617970912018780043
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7803168780329886017}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 2439271445520760248}
+ m_Father: {fileID: 1457871284126546349}
+ m_RootOrder: 4
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &5771162771103583132
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7803168780329886017}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _interpolationDataSource: 0
+ InterpolationSpace: 0
+ InterpolationTarget: {fileID: 2439271445520760248}
+ InterpolateErrorCorrection: 1
+ InterpolatedErrorCorrectionSettings:
+ MinRate: 3.3
+ MaxRate: 10
+ PosBlendStart: 0.25
+ PosBlendEnd: 1
+ PosMinCorrection: 0.025
+ PosTeleportDistance: 2
+ RotBlendStart: 0.1
+ RotBlendEnd: 0.5
+ RotTeleportRadians: 1.5
+ UseLegacySharedModeInterpolation: 0
+ TargetInterpolationDelay: 0.03
+--- !u!1 &8601996063017238699
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1457871284126546349}
+ m_Layer: 0
+ m_Name: DATA_TRANSFERS
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1457871284126546349
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8601996063017238699}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 6539275587195953435}
+ - {fileID: 1030654070696327489}
+ - {fileID: 5887326793658353859}
+ - {fileID: 7668165333132264083}
+ - {fileID: 8617970912018780043}
+ m_Father: {fileID: 6718544642020818535}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &8742815544225261104
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2514485848220458462}
+ m_Layer: 0
+ m_Name: DummyHead
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2514485848220458462
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8742815544225261104}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1030654070696327489}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &8559495263563694728
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 5041384770245166357}
+ m_Modifications:
+ - target: {fileID: 1448111865499013753, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_Name
+ value: HMDMesh
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalScale.x
+ value: 12
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalScale.y
+ value: 12
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalScale.z
+ value: 12
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: -0.45
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 834f4ac71ec35d148acacfca28f04405, type: 3}
+--- !u!4 &7120707258077581554 stripped
+Transform:
+ m_CorrespondingSourceObject: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ m_PrefabInstance: {fileID: 8559495263563694728}
+ m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab.meta b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab.meta
new file mode 100644
index 0000000000..726ed433ed
--- /dev/null
+++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9ee52735aebb6a445b4905c8aba8cfe3
+labels:
+- FusionPrefab
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/OculusRuntimeSettings.asset b/Assets/Resources/OculusRuntimeSettings.asset
index b5b403bd88..0dde6eca9a 100644
--- a/Assets/Resources/OculusRuntimeSettings.asset
+++ b/Assets/Resources/OculusRuntimeSettings.asset
@@ -13,3 +13,6 @@ MonoBehaviour:
m_Name: OculusRuntimeSettings
m_EditorClassIdentifier:
colorSpace: 4
+ hasSentConsentEvent: 1
+ hasSetTelemetryEnabled: 1
+ telemetryEnabled: 0
diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity
index 25b9bb36d5..43763876e5 100644
--- a/Assets/Scenes/Main.unity
+++ b/Assets/Scenes/Main.unity
@@ -10123,6 +10123,7 @@ Transform:
- {fileID: 1412532063}
- {fileID: 383974627}
- {fileID: 669339392}
+ - {fileID: 1052269831}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -13585,89 +13586,24 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 948093179}
m_CullTransparentMesh: 0
---- !u!1 &950303573
+--- !u!1 &950303573 stripped
GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
+ m_CorrespondingSourceObject: {fileID: 1448111865499013753, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ m_PrefabInstance: {fileID: 1448111864570226988}
m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 950303574}
- - component: {fileID: 950303576}
- - component: {fileID: 950303575}
- m_Layer: 15
- m_Name: DropCamHeadMesh
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!4 &950303574
+--- !u!4 &950303574 stripped
Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
+ m_CorrespondingSourceObject: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ m_PrefabInstance: {fileID: 1448111864570226988}
m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 950303573}
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 0, y: 0, z: -0.15}
- m_LocalScale: {x: 4, y: 4, z: 4}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 1087863899}
- m_RootOrder: 1
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!23 &950303575
+--- !u!23 &950303575 stripped
MeshRenderer:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 950303573}
- m_Enabled: 1
- m_CastShadows: 0
- m_ReceiveShadows: 0
- m_DynamicOccludee: 1
- m_StaticShadowCaster: 0
- m_MotionVectors: 1
- m_LightProbeUsage: 0
- m_ReflectionProbeUsage: 1
- m_RayTracingMode: 2
- m_RayTraceProcedural: 0
- m_RenderingLayerMask: 1
- m_RendererPriority: 0
- m_Materials:
- - {fileID: 2100000, guid: 981fe2686b81af74ca426ec05bdfe481, type: 2}
- m_StaticBatchInfo:
- firstSubMesh: 0
- subMeshCount: 0
- m_StaticBatchRoot: {fileID: 0}
- m_ProbeAnchor: {fileID: 0}
- m_LightProbeVolumeOverride: {fileID: 0}
- m_ScaleInLightmap: 1
- m_ReceiveGI: 1
- m_PreserveUVs: 1
- m_IgnoreNormalsForChartDetection: 0
- m_ImportantGI: 0
- m_StitchLightmapSeams: 0
- m_SelectedEditorRenderState: 3
- m_MinimumChartSize: 4
- m_AutoUVMaxDistance: 0.5
- m_AutoUVMaxAngle: 89
- m_LightmapParameters: {fileID: 0}
- m_SortingLayerID: 0
- m_SortingLayer: 0
- m_SortingOrder: 0
- m_AdditionalVertexStreams: {fileID: 0}
---- !u!33 &950303576
-MeshFilter:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
+ m_CorrespondingSourceObject: {fileID: 1448111865499013755, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ m_PrefabInstance: {fileID: 1448111864570226988}
m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 950303573}
- m_Mesh: {fileID: 4300000, guid: dd4ebe901b35bd949a7116ab582586b9, type: 3}
--- !u!1 &952277096
GameObject:
m_ObjectHideFlags: 0
@@ -15023,6 +14959,51 @@ MeshFilter:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1052118967}
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &1052269830
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1052269831}
+ - component: {fileID: 1052269832}
+ m_Layer: 0
+ m_Name: MultiplayerManager
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1052269831
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1052269830}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 652605545}
+ m_RootOrder: 4
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1052269832
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1052269830}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: b989d2f19099ac044b955d3a6a62dea9, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_MultiplayerType: 2
--- !u!1 &1057179852
GameObject:
m_ObjectHideFlags: 0
@@ -35289,6 +35270,90 @@ MeshFilter:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1188089478511272227}
m_Mesh: {fileID: 4300000, guid: 5ef960ddf11c1fd4983638f56f6a8be0, type: 3}
+--- !u!1001 &1448111864570226988
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 1087863899}
+ m_Modifications:
+ - target: {fileID: 1448111865499013753, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_Name
+ value: DropCamHeadMesh
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalScale.x
+ value: 4
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalScale.y
+ value: 4
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalScale.z
+ value: 4
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: -0.15
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 834f4ac71ec35d148acacfca28f04405, type: 3}
--- !u!4 &1518196148397455185
Transform:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/Commands/BaseCommand.cs b/Assets/Scripts/Commands/BaseCommand.cs
index ee38204460..45492bcf57 100644
--- a/Assets/Scripts/Commands/BaseCommand.cs
+++ b/Assets/Scripts/Commands/BaseCommand.cs
@@ -26,8 +26,30 @@ namespace TiltBrush
/// the base class or be sure to recurse into children.
public class BaseCommand : IDisposable
{
+ private Guid m_Guid;
+ private BaseCommand m_Parent;
protected List m_Children;
+ public int ChildrenCount
+ {
+ get { return m_Children.Count; }
+ }
+
+ public Guid Guid
+ {
+ get { return m_Guid; }
+ }
+
+ public List Children
+ {
+ get { return m_Children.ToList(); }
+ }
+
+ public Guid ParentGuid
+ {
+ get { return m_Parent != null ? m_Parent.Guid : Guid.Empty; }
+ }
+
protected static Vector3 GetPositionForCommand(Stroke stroke)
{
var points = stroke.m_ControlPoints;
@@ -48,10 +70,12 @@ protected static Vector3 GetPositionForCommand(Stroke[] strokes)
/// The constructor should not mutate sketch state.
public BaseCommand(BaseCommand parent = null)
{
+ m_Guid = Guid.NewGuid();
m_Children = new List();
if (parent != null)
{
parent.m_Children.Add(this);
+ m_Parent = parent;
}
}
@@ -111,6 +135,11 @@ public virtual bool Merge(BaseCommand other)
return other.IsNoop;
}
+ public virtual string Serialize()
+ {
+ return string.Empty;
+ }
+
/// Dispose of this entire command tree if there are any
/// controlled resources.
/// Parent is always destroyed after all children.
diff --git a/Assets/Scripts/Commands/BrushStrokeCommand.cs b/Assets/Scripts/Commands/BrushStrokeCommand.cs
index bf606b1d5b..3cce0caa6b 100644
--- a/Assets/Scripts/Commands/BrushStrokeCommand.cs
+++ b/Assets/Scripts/Commands/BrushStrokeCommand.cs
@@ -18,7 +18,7 @@ namespace TiltBrush
{
public class BrushStrokeCommand : BaseCommand
{
- private Stroke m_Stroke;
+ public Stroke m_Stroke;
private StencilWidget m_Widget;
private float m_LineLength_CS; // Only valid if m_Widget != null
@@ -35,6 +35,13 @@ public BrushStrokeCommand(Stroke stroke, StencilWidget widget = null,
m_LineLength_CS = lineLength;
}
+ public override string Serialize()
+ {
+ //var data = Newtonsoft.Json.JsonConvert.SerializeObject(m_Stroke);
+ //UnityEngine.Debug.Log($"test: {data}");
+ return JsonUtility.ToJson(m_Stroke);
+ }
+
public override bool NeedsSave { get { return true; } }
protected override void OnDispose()
diff --git a/Assets/Scripts/Commands/DeleteStrokeCommand.cs b/Assets/Scripts/Commands/DeleteStrokeCommand.cs
index 1250775d89..18f86c1d30 100644
--- a/Assets/Scripts/Commands/DeleteStrokeCommand.cs
+++ b/Assets/Scripts/Commands/DeleteStrokeCommand.cs
@@ -18,7 +18,7 @@ namespace TiltBrush
{
public class DeleteStrokeCommand : BaseCommand
{
- private Stroke m_TargetStroke;
+ public Stroke m_TargetStroke;
private bool m_SilenceFirstAudio;
private Vector3 CommandAudioPosition
diff --git a/Assets/Scripts/Config.cs b/Assets/Scripts/Config.cs
index bed15a24db..fecf321117 100644
--- a/Assets/Scripts/Config.cs
+++ b/Assets/Scripts/Config.cs
@@ -129,6 +129,7 @@ private class UserConfigChange
public SecretsConfig.ServiceAuthData OculusSecrets => Secrets[SecretsConfig.Service.Oculus];
public SecretsConfig.ServiceAuthData OculusMobileSecrets => Secrets[SecretsConfig.Service.OculusMobile];
public SecretsConfig.ServiceAuthData PimaxSecrets => Secrets[SecretsConfig.Service.Pimax];
+ public SecretsConfig.ServiceAuthData PhotonFusionSecrets => Secrets[SecretsConfig.Service.PhotonFusion];
public bool DisableAccountLogins;
diff --git a/Assets/Scripts/Multiplayer.meta b/Assets/Scripts/Multiplayer.meta
new file mode 100644
index 0000000000..8b76c079e2
--- /dev/null
+++ b/Assets/Scripts/Multiplayer.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 954339fc67e50a54986118b6ac59ee38
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs
new file mode 100644
index 0000000000..2401d272f0
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs
@@ -0,0 +1,49 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace OpenBrush.Multiplayer
+{
+ [System.Serializable]
+ public struct PlayerRigData
+ {
+ public Vector3 HeadPosition;
+ public Quaternion HeadRotation;
+ public Vector3 HeadScale;
+
+ public Vector3 ToolPosition;
+ public Quaternion ToolRotation;
+
+ public BrushData BrushData;
+ public ExtraData ExtraData;
+
+ }
+
+ [System.Serializable]
+ public struct BrushData
+ {
+ public Color Color;
+ public string Guid;
+ public float Size;
+ }
+
+ [System.Serializable]
+ public struct ExtraData
+ {
+ public ulong OculusPlayerId;
+ }
+}
diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs.meta b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs.meta
new file mode 100644
index 0000000000..4851bff5c7
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fd96ac52cd212b3418729495065a5603
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs
new file mode 100644
index 0000000000..a58eeb5210
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs
@@ -0,0 +1,45 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+using TiltBrush;
+using UnityEngine;
+
+namespace OpenBrush.Multiplayer
+{
+ public interface IConnectionHandler
+ {
+ Task Connect();
+
+ bool IsConnected();
+ Task Disconnect(bool force = false);
+
+ void Update();
+
+ Task PerformCommand(BaseCommand command);
+ Task UndoCommand(BaseCommand command);
+ Task RedoCommand(BaseCommand command);
+ Task RpcSyncToSharedAnchor(string uuid);
+
+ //ITransientData SpawnPlayer();
+ }
+
+ public interface ITransientData
+ {
+ void TransmitData(T data);
+ T RecieveData();
+ }
+}
diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs.meta b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs.meta
new file mode 100644
index 0000000000..be153a0983
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a8a716423ae9ed34087163f00bfe3ca2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs
new file mode 100644
index 0000000000..71c42aff0e
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs
@@ -0,0 +1,225 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using Unity.XR.CoreUtils;
+using OVRPlatform = Oculus.Platform;
+using TiltBrush;
+
+namespace OpenBrush.Multiplayer
+{
+ public enum MultiplayerType
+ {
+ None,
+ Colyseus = 1,
+ Photon = 2,
+ }
+
+ public class MultiplayerManager : MonoBehaviour
+ {
+ public static MultiplayerManager m_Instance;
+ public MultiplayerType m_MultiplayerType;
+
+ private IConnectionHandler m_Manager;
+
+ private ITransientData m_LocalPlayer;
+ private List> m_RemotePlayers;
+
+ public Action> localPlayerJoined;
+ public Action> remotePlayerJoined;
+
+ ulong myOculusUserId;
+
+ List oculusPlayerIds;
+
+ private bool IsConnected { get { return m_Manager != null && m_Manager.IsConnected(); } }
+
+ void Awake()
+ {
+ m_Instance = this;
+ oculusPlayerIds = new List();
+ m_RemotePlayers = new List>();
+ }
+
+ void Start()
+ {
+
+#if OCULUS_SUPPORTED
+ OVRPlatform.Users.GetLoggedInUser().OnComplete((msg) => {
+ if (!msg.IsError)
+ {
+ myOculusUserId = msg.GetUser().ID;
+ Debug.Log($"OculusID: {myOculusUserId}");
+ oculusPlayerIds.Add(myOculusUserId);
+ }
+ else
+ {
+ Debug.LogError(msg.GetError());
+ }
+ });
+#endif
+ switch (m_MultiplayerType)
+ {
+ case MultiplayerType.Photon:
+#if FUSION_WEAVER
+ m_Manager = new PhotonManager(this);
+#endif // FUSION_WEAVER
+ break;
+ default:
+ return;
+ }
+
+ localPlayerJoined += OnLocalPlayerJoined;
+ remotePlayerJoined += OnRemotePlayerJoined;
+ SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed;
+ SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo;
+ SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo;
+ }
+
+ void OnDestroy()
+ {
+ localPlayerJoined -= OnLocalPlayerJoined;
+ remotePlayerJoined -= OnRemotePlayerJoined;
+ SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed;
+ SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo;
+ SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo;
+ }
+
+ public async void Connect()
+ {
+ var result = await m_Manager.Connect();
+ }
+
+ void Update()
+ {
+ if (App.CurrentState != App.AppState.Standard || m_Manager == null)
+ {
+ return;
+ }
+
+ m_Manager.Update();
+
+ // Transmit local player data relative to scene origin
+ var headRelativeToScene = App.Scene.AsScene[App.VrSdk.GetVrCamera().transform];
+ var pointerRelativeToScene = App.Scene.AsScene[PointerManager.m_Instance.MainPointer.transform];
+
+ var data = new PlayerRigData
+ {
+ HeadPosition = headRelativeToScene.translation,
+ HeadRotation = headRelativeToScene.rotation,
+ ToolPosition = pointerRelativeToScene.translation,
+ ToolRotation = pointerRelativeToScene.rotation,
+ BrushData = new BrushData
+ {
+ Color = PointerManager.m_Instance.MainPointer.GetCurrentColor(),
+ Size = PointerManager.m_Instance.MainPointer.BrushSize01,
+ Guid = BrushController.m_Instance.ActiveBrush.m_Guid.ToString(),
+ },
+ ExtraData = new ExtraData
+ {
+ OculusPlayerId = myOculusUserId,
+ }
+ };
+
+ if (m_LocalPlayer != null)
+ {
+ m_LocalPlayer.TransmitData(data);
+ }
+
+
+ // Update remote user refs, and send Anchors if new player joins.
+ bool newUser = false;
+ foreach (var player in m_RemotePlayers)
+ {
+ data = player.RecieveData();
+ // New user, share the anchor with them
+ if (data.ExtraData.OculusPlayerId != 0 && !oculusPlayerIds.Contains(data.ExtraData.OculusPlayerId))
+ {
+ Debug.Log("detected new user!");
+ Debug.Log(data.ExtraData.OculusPlayerId);
+ oculusPlayerIds.Add(data.ExtraData.OculusPlayerId);
+ newUser = true;
+ }
+ }
+
+ if (newUser)
+ {
+ ShareAnchors();
+ }
+ }
+
+ void OnLocalPlayerJoined(ITransientData playerData)
+ {
+ m_LocalPlayer = playerData;
+ }
+
+ void OnRemotePlayerJoined(ITransientData playerData)
+ {
+ Debug.Log("Adding new player to track.");
+ m_RemotePlayers.Add(playerData);
+ }
+
+ private async void OnCommandPerformed(BaseCommand command)
+ {
+ if (!IsConnected)
+ {
+ return;
+ }
+
+ var success = await m_Manager.PerformCommand(command);
+
+ // TODO: Proper rollback if command not possible right now.
+ // Commented so it doesn't interfere with general use.
+ // Link actions to connect/disconnect, not Unity lifecycle.
+
+ // if (!success)
+ // {
+ // OutputWindowScript.m_Instance.CreateInfoCardAtController(InputManager.ControllerName.Brush, "Don't know how to network this action yet.");
+ // SketchMemoryScript.m_Instance.StepBack(false);
+ // }
+ }
+
+ private void OnCommandUndo(BaseCommand command)
+ {
+ if (IsConnected)
+ {
+ m_Manager.UndoCommand(command);
+ }
+ }
+
+ private void OnCommandRedo(BaseCommand command)
+ {
+ if (IsConnected)
+ {
+ m_Manager.RedoCommand(command);
+ }
+ }
+
+ async void ShareAnchors()
+ {
+ Debug.Log($"sharing to {oculusPlayerIds.Count} Ids");
+ var success = await OculusMRController.m_Instance.m_SpatialAnchorManager.ShareAnchors(oculusPlayerIds);
+
+ if (success)
+ {
+ if (!OculusMRController.m_Instance.m_SpatialAnchorManager.AnchorUuid.Equals(String.Empty))
+ {
+ await m_Manager.RpcSyncToSharedAnchor(OculusMRController.m_Instance.m_SpatialAnchorManager.AnchorUuid);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs.meta b/Assets/Scripts/Multiplayer/MultiplayerManager.cs.meta
new file mode 100644
index 0000000000..df8e11806b
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b989d2f19099ac044b955d3a6a62dea9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/Photon.meta b/Assets/Scripts/Multiplayer/Photon.meta
new file mode 100644
index 0000000000..394aac8f44
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 19da2d520bc5adb4d965967d074e2a48
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs
new file mode 100644
index 0000000000..579b06bc88
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs
@@ -0,0 +1,281 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if FUSION_WEAVER
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using UnityEngine;
+using Fusion;
+using Fusion.Photon.Realtime;
+using Fusion.Sockets;
+using TiltBrush;
+using System.Linq;
+
+namespace OpenBrush.Multiplayer
+{
+ public class PhotonManager : IConnectionHandler, INetworkRunnerCallbacks
+ {
+ private NetworkRunner m_Runner;
+
+ MultiplayerManager m_Manager;
+
+ List m_PlayersSpawning;
+
+ PhotonPlayerRig m_LocalPlayer;
+
+ public PhotonManager(MultiplayerManager manager)
+ {
+ m_Manager = manager;
+ m_PlayersSpawning = new List();
+ }
+
+ public async Task Connect()
+ {
+ if(m_Runner != null)
+ {
+ GameObject.Destroy(m_Runner);
+ }
+
+ var runnerGO = new GameObject("Photon Network Components");
+
+ m_Runner = runnerGO.AddComponent();
+ m_Runner.ProvideInput = true;
+ m_Runner.AddCallbacks(this);
+
+ var appSettings = new AppSettings
+ {
+ AppIdFusion = App.Config.PhotonFusionSecrets.ClientId,
+ // Need this set for some reason
+ FixedRegion = "",
+ };
+
+ var args = new StartGameArgs()
+ {
+ GameMode = GameMode.Shared,
+ SessionName = "OpenBrushMultiplayerTest",
+ CustomPhotonAppSettings = appSettings,
+ SceneManager = m_Runner.gameObject.AddComponent(),
+ Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex,
+ };
+
+ var result = await m_Runner.StartGame(args);
+
+ return result.Ok;
+
+ }
+
+ public bool IsConnected()
+ {
+ if(m_Runner == null)
+ {
+ return false;
+ }
+ return m_Runner.IsRunning;
+ }
+
+ public async Task Disconnect(bool force)
+ {
+ if(m_Runner != null)
+ {
+ await m_Runner.Shutdown(forceShutdownProcedure: force);
+ return m_Runner.IsShutdown;
+ }
+ return true;
+ }
+
+ public void Update()
+ {
+ var copy = m_PlayersSpawning.ToList();
+ foreach (var player in copy)
+ {
+ var newPlayer = m_Runner.GetPlayerObject(player);
+ if (newPlayer != null)
+ {
+ m_Manager.remotePlayerJoined?.Invoke(newPlayer.GetComponent());
+ m_PlayersSpawning.Remove(player);
+ }
+ }
+ }
+
+#region IConnectionHandler Methods
+ public async Task PerformCommand(BaseCommand command)
+ {
+ await Task.Yield();
+ return ProcessCommand(command);;
+ }
+
+ public async Task UndoCommand(BaseCommand command)
+ {
+ PhotonRPC.RPC_Undo(m_Runner, command.GetType().ToString());
+ await Task.Yield();
+ return true;
+ }
+
+ public async Task RedoCommand(BaseCommand command)
+ {
+ PhotonRPC.RPC_Redo(m_Runner, command.GetType().ToString());
+ await Task.Yield();
+ return true;
+ }
+
+ public async Task RpcSyncToSharedAnchor(string uuid)
+ {
+ PhotonRPC.RPC_SyncToSharedAnchor(m_Runner, uuid);
+ await Task.Yield();
+ return true;
+ }
+#endregion
+
+#region Command Methods
+ private bool ProcessCommand(BaseCommand command)
+ {
+ bool success = true;
+ switch(command)
+ {
+ case BrushStrokeCommand:
+ success = CommandBrushStroke(command as BrushStrokeCommand);
+ break;
+ case DeleteStrokeCommand:
+ success = CommandDeleteStroke(command as DeleteStrokeCommand);
+ break;
+ case BaseCommand:
+ success = CommandBase(command);
+ break;
+ default:
+ // Don't know how to process this command
+ success = false;
+ break;
+ }
+
+ if(command.ChildrenCount > 0)
+ {
+ foreach(var child in command.Children)
+ {
+ success &= ProcessCommand(child);
+ }
+ }
+
+ return success;
+ }
+
+ private bool CommandBrushStroke(BrushStrokeCommand command)
+ {
+ var stroke = command.m_Stroke;
+
+ if (stroke.m_ControlPoints.Length > 128)
+ {
+ // Split and Send
+ int numSplits = stroke.m_ControlPoints.Length / 128;
+
+ var firstStroke = new Stroke(stroke)
+ {
+ m_ControlPoints = stroke.m_ControlPoints.Take(128).ToArray(),
+ m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(128).ToArray()
+ };
+
+ var netStroke = new NetworkedStroke().Init(firstStroke);
+
+ var strokeGuid = Guid.NewGuid();
+
+ // First Stroke
+ PhotonRPC.RPC_BrushStrokeBegin(m_Runner, strokeGuid, netStroke, stroke.m_ControlPoints.Length);
+
+ // Middle
+ for (int rounds = 1; rounds < numSplits + 1; ++rounds)
+ {
+ var controlPoints = stroke.m_ControlPoints.Skip(rounds*128).Take(128).ToArray();
+ var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds*128).Take(128).ToArray();
+
+ var netControlPoints = new NetworkedControlPoint[controlPoints.Length];
+
+ for (int point = 0; point < controlPoints.Length; ++ point)
+ {
+ netControlPoints[point] = new NetworkedControlPoint().Init(controlPoints[point]);
+ }
+
+ PhotonRPC.RPC_BrushStrokeContinue(m_Runner, strokeGuid, rounds * 128, netControlPoints, dropPoints);
+ }
+
+ // End
+ PhotonRPC.RPC_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, command.ParentGuid, command.ChildrenCount);
+ }
+ else
+ {
+ // Can send in one.
+ PhotonRPC.RPC_BrushStrokeFull(m_Runner, new NetworkedStroke().Init(command.m_Stroke), command.Guid, command.ParentGuid, command.ChildrenCount);
+ }
+ return true;
+ }
+
+ private bool CommandBase(BaseCommand command)
+ {
+ PhotonRPC.RPC_BaseCommand(m_Runner, command.Guid, command.ParentGuid, command.ChildrenCount);
+ return true;
+ }
+
+ private bool CommandDeleteStroke(DeleteStrokeCommand command)
+ {
+ PhotonRPC.RPC_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, command.ParentGuid, command.ChildrenCount);
+ return true;
+ }
+#endregion
+
+#region Photon Callbacks
+ public void OnConnectedToServer(NetworkRunner runner)
+ {
+ var rpc = m_Runner.gameObject.AddComponent();
+ m_Runner.AddSimulationBehaviour(rpc);
+ }
+
+ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
+ {
+ if(player == m_Runner.LocalPlayer)
+ {
+ var playerPrefab = Resources.Load("Multiplayer/Photon/PhotonPlayerRig") as GameObject;
+ var playerObj = m_Runner.Spawn(playerPrefab, inputAuthority: m_Runner.LocalPlayer);
+ m_LocalPlayer = playerObj.GetComponent();
+ m_Runner.SetPlayerObject(m_Runner.LocalPlayer, playerObj);
+
+
+ m_Manager.localPlayerJoined?.Invoke(m_LocalPlayer);
+ }
+ else
+ {
+ m_PlayersSpawning.Add(player);
+ }
+ }
+#endregion
+
+#region Unused Photon Callbacks
+ public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) { }
+ public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { }
+ public void OnDisconnectedFromServer(NetworkRunner runner) { }
+ public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { }
+ public void OnInput(NetworkRunner runner, NetworkInput input) { }
+ public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
+ public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) { }
+ public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { }
+ public void OnSessionListUpdated(NetworkRunner runner, List sessionList) { }
+ public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary data) { }
+ public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { }
+ public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment data) { }
+ public void OnSceneLoadDone(NetworkRunner runner) { }
+ public void OnSceneLoadStart(NetworkRunner runner) { }
+#endregion
+ }
+}
+
+#endif // FUSION_WEAVER
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs.meta b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs.meta
new file mode 100644
index 0000000000..c84d889790
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2194b426b1940e944a32c05b1dac9a4a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs
new file mode 100644
index 0000000000..40a9e81912
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs
@@ -0,0 +1,122 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if FUSION_WEAVER
+
+using UnityEngine;
+using Fusion;
+using TiltBrush;
+
+namespace OpenBrush.Multiplayer
+{
+ public class PhotonPlayerRig : NetworkBehaviour, ITransientData
+ {
+ // Only used for transferring data - don't actually use these transforms without offsetting
+ public NetworkTransform m_PlayArea;
+ public NetworkTransform m_PlayerHead;
+ public NetworkTransform m_Left;
+ public NetworkTransform m_Right;
+ public NetworkTransform m_Tool;
+
+ [Networked] private Color brushColor { get; set; }
+ [Networked] private float brushSize { get; set; }
+ [Networked] private NetworkString<_64> brushGuid { get; set; }
+ [Networked] public ulong oculusPlayerId { get; set; }
+
+ PointerScript transientPointer;
+ // The offset transforms.
+ [SerializeField] private Transform headTransform;
+ private PlayerRigData transmitData;
+
+ public void TransmitData(PlayerRigData data)
+ {
+ transmitData = data;
+ oculusPlayerId = data.ExtraData.OculusPlayerId;
+
+ brushColor = data.BrushData.Color;
+ brushSize = data.BrushData.Size;
+ brushGuid = data.BrushData.Guid;
+ }
+
+ public PlayerRigData RecieveData()
+ {
+ var data = new PlayerRigData
+ {
+ HeadPosition = m_PlayerHead.InterpolationTarget.position,
+ HeadRotation = m_PlayerHead.InterpolationTarget.rotation,
+ ExtraData = new ExtraData
+ {
+ OculusPlayerId = this.oculusPlayerId
+ }
+ };
+ return data;
+ }
+
+ public override void Spawned()
+ {
+ base.Spawned();
+
+ brushGuid = BrushCatalog.m_Instance.DefaultBrush.m_Guid.ToString();
+
+ if(!Object.HasStateAuthority)
+ {
+ transientPointer = PointerManager.m_Instance.CreateRemotePointer();
+ transientPointer.SetBrush(BrushCatalog.m_Instance.DefaultBrush);
+ transientPointer.SetColor(App.BrushColor.CurrentColor);
+ }
+ }
+
+ public override void FixedUpdateNetwork()
+ {
+ base.FixedUpdateNetwork();
+
+ if(Object.HasStateAuthority)
+ {
+ m_PlayerHead.transform.position = transmitData.HeadPosition;
+ m_PlayerHead.transform.rotation = transmitData.HeadRotation;
+
+ m_Tool.transform.position = transmitData.ToolPosition;
+ m_Tool.transform.rotation = transmitData.ToolRotation;
+ }
+ }
+
+ public override void Render()
+ {
+ base.Render();
+
+ if (Object.HasStateAuthority)
+ {
+
+ }
+
+ else
+ {
+ var toolTR = TrTransform.TR(m_Tool.InterpolationTarget.position, m_Tool.InterpolationTarget.rotation);
+ App.Scene.AsScene[transientPointer.transform] = toolTR;
+
+ transientPointer.SetColor(brushColor);
+ if(brushGuid.ToString() != string.Empty)
+ {
+ transientPointer.SetBrush(BrushCatalog.m_Instance.GetBrush(new System.Guid(brushGuid.ToString())));
+ }
+ transientPointer.BrushSize01 = brushSize;
+ }
+
+ var remoteTR = TrTransform.TR(m_PlayerHead.InterpolationTarget.position, m_PlayerHead.InterpolationTarget.rotation);
+ App.Scene.AsScene[headTransform] = remoteTR;
+ }
+ }
+}
+
+#endif // FUSION_WEAVER
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs.meta b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs.meta
new file mode 100644
index 0000000000..524e97163e
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f250b0d6a1df335439813ad43ce3dbdb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs
new file mode 100644
index 0000000000..477276e880
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs
@@ -0,0 +1,298 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#if FUSION_WEAVER
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using UnityEngine;
+using Fusion;
+using TiltBrush;
+
+namespace OpenBrush.Multiplayer
+{
+ public class PhotonRPC : SimulationBehaviour
+ {
+ private static Dictionary m_inProgressStrokes;
+ private static List m_pendingCommands;
+
+ public void Awake()
+ {
+ m_inProgressStrokes = new();
+ m_pendingCommands = new();
+ }
+
+ public void Update()
+ {
+ TryProcessCommands();
+ }
+
+ private bool CheckifChildStillPending(PendingCommand pending)
+ {
+ if (pending.TotalExpectedChildren == pending.Command.ChildrenCount)
+ {
+ bool moreChildrenToAssign = false;
+
+ foreach (var childCommand in pending.Command.Children)
+ {
+ // has a child present in the pending queue, check them too
+ var childPending = m_pendingCommands.FirstOrDefault(x => x.Guid == childCommand.Guid);
+
+ if (!childPending.Guid.Equals(default))
+ {
+ var childIsStillPending = CheckifChildStillPending(childPending);
+
+ if (!childIsStillPending)
+ {
+ m_pendingCommands.Remove(childPending);
+ }
+
+ moreChildrenToAssign |= childIsStillPending;
+ }
+ }
+
+ return moreChildrenToAssign;
+ }
+
+ else
+ {
+ return true;
+ }
+ }
+
+ private void InvokePreCommands(PendingCommand pendingCommand)
+ {
+ pendingCommand.PreCommandAction.Invoke();
+
+ foreach (var childCommand in pendingCommand.ChildCommands)
+ {
+ InvokePreCommands(childCommand);
+ }
+ }
+
+ private void TryProcessCommands()
+ {
+ if (m_pendingCommands.Count == 0)
+ {
+ return;
+ }
+
+ var command = m_pendingCommands[0];
+
+ bool stillPending = CheckifChildStillPending(command);
+
+ if (stillPending)
+ {
+ return;
+ }
+
+ // All children present, begin execution
+
+ m_pendingCommands.RemoveAt(0);
+
+ InvokePreCommands(command);
+
+
+ SketchMemoryScript.m_Instance.PerformAndRecordCommand(command.Command, invoke: false);
+
+ TryProcessCommands();
+ }
+
+ private static void AddPendingCommand(Action preAction, Guid commandGuid, Guid parentGuid, BaseCommand command, int childCount)
+ {
+ PendingCommand pendingCommand = new PendingCommand(commandGuid, command, preAction, childCount);
+
+ if (!parentGuid.Equals(default))
+ {
+ var pendingParent = m_pendingCommands.FirstOrDefault(x => x.Guid == parentGuid);
+ pendingParent.ChildCommands.Add(pendingCommand);
+ }
+
+ m_pendingCommands.Add(pendingCommand);
+ }
+
+ private static BaseCommand FindParentCommand(Guid parentGuid)
+ {
+ PendingCommand pendingParent = m_pendingCommands.FirstOrDefault(x => x.Guid == parentGuid);
+
+ if (!parentGuid.Equals(default))
+ {
+ return pendingParent.Command;
+ }
+ return null;
+ }
+
+ public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, Guid parentGuid = default, int childCount = 0)
+ {
+ Action preAction = () =>
+ {
+ stroke.m_Type = Stroke.Type.NotCreated;
+ stroke.m_IntendedCanvas = App.Scene.MainCanvas;
+ stroke.Recreate(null, App.Scene.MainCanvas);
+ SketchMemoryScript.m_Instance.MemoryListAdd(stroke);
+ };
+
+ var parentCommand = FindParentCommand(parentGuid);
+
+ var command = new BrushStrokeCommand(stroke, parent: parentCommand);
+
+ AddPendingCommand(preAction, commandGuid, parentGuid, command, childCount);
+ }
+
+#region RPCS
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid)
+ {
+ OculusMRController.m_Instance.RemoteSyncToAnchor(uuid);
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_PerformCommand(NetworkRunner runner, string commandName, string guid, string[] data)
+ {
+ Debug.Log($"Command recieved: {commandName}");
+ if (commandName.Equals("TiltBrush.BrushStrokeCommand"))
+ {
+ var asString = string.Join(string.Empty, data);
+ Debug.Log(asString);
+ var decode = JsonUtility.FromJson(asString);
+
+ // Temp
+ decode.m_BrushGuid = new System.Guid(guid);
+
+ // Can we set up these more sensibly?
+ decode.m_Type = Stroke.Type.NotCreated;
+ decode.m_IntendedCanvas = App.Scene.MainCanvas;
+
+ // Setup data that couldn't be transferred
+ decode.Recreate(null, App.Scene.MainCanvas);
+ SketchMemoryScript.m_Instance.MemoryListAdd(decode);
+
+ SketchMemoryScript.m_Instance.PerformAndRecordCommand(new BrushStrokeCommand(decode), invoke: false);
+ }
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_Undo(NetworkRunner runner, string commandName)
+ {
+ if (SketchMemoryScript.m_Instance.CanUndo())
+ {
+ SketchMemoryScript.m_Instance.StepBack(false);
+ }
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_Redo(NetworkRunner runner, string commandName)
+ {
+ if (SketchMemoryScript.m_Instance.CanRedo())
+ {
+ SketchMemoryScript.m_Instance.StepForward(false);
+ }
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0)
+ {
+ Debug.Log($"Base command child count: {childCount}");
+ var parentCommand = FindParentCommand(parentGuid);
+ var command = new BaseCommand(parent: parentCommand);
+
+ AddPendingCommand(() => {}, commandGuid, parentGuid, command, childCount);
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, Guid parentGuid = default, int childCount = 0)
+ {
+ var decode = NetworkedStroke.ToStroke(strokeData);
+
+ CreateBrushStroke(decode, commandGuid, parentGuid, childCount);
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength)
+ {
+ var decode = NetworkedStroke.ToStroke(strokeData);
+
+ decode.m_Type = Stroke.Type.NotCreated;
+ decode.m_IntendedCanvas = App.Scene.MainCanvas;
+
+ Array.Resize(ref decode.m_ControlPoints, finalLength);
+ Array.Resize(ref decode.m_ControlPointsToDrop, finalLength);
+
+ if(m_inProgressStrokes.ContainsKey(id))
+ {
+ Debug.LogError("Shouldn't be here!");
+ return;
+ }
+
+ m_inProgressStrokes[id] = decode;
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints)
+ {
+ if(!m_inProgressStrokes.ContainsKey(id))
+ {
+ Debug.LogError("shouldn't be here!");
+ return;
+ }
+
+ var stroke = m_inProgressStrokes[id];
+
+ for(int i = 0; i < controlPoints.Length; ++i)
+ {
+ stroke.m_ControlPoints[offset + i] = NetworkedControlPoint.ToControlPoint(controlPoints[i]);
+ stroke.m_ControlPointsToDrop[offset + i] = dropPoints[i];
+ }
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, Guid parentGuid = default, int childCount = 0)
+ {
+ if(!m_inProgressStrokes.ContainsKey(id))
+ {
+ Debug.LogError("shouldn't be here!");
+ return;
+ }
+
+ var stroke = m_inProgressStrokes[id];
+
+ CreateBrushStroke(stroke, commandGuid, parentGuid, childCount);
+
+ m_inProgressStrokes.Remove(id);
+ }
+
+ [Rpc(InvokeLocal = false)]
+ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, Guid parentGuid = default, int childCount = 0)
+ {
+ var foundStroke = SketchMemoryScript.m_Instance.GetMemoryList.Where(x => x.m_Seed == seed).First();
+
+ if (foundStroke != null)
+ {
+ var parentCommand = FindParentCommand(parentGuid);
+ var command = new DeleteStrokeCommand(foundStroke, parent: parentCommand);
+
+ AddPendingCommand(() => {}, commandGuid, parentGuid, command, childCount);
+ }
+ else
+ {
+ Debug.LogError($"couldn't find stroke with seed: {seed}");
+ }
+ }
+#endregion
+ }
+}
+
+#endif // FUSION_WEAVER
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs.meta b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs.meta
new file mode 100644
index 0000000000..6b532f35b6
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 411fb1979ee6c644cbe60df50efacfd7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs
new file mode 100644
index 0000000000..10d6787740
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs
@@ -0,0 +1,171 @@
+// Copyright 2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if FUSION_WEAVER
+
+using System;
+using UnityEngine;
+using Fusion;
+using TiltBrush;
+using System.Collections.Generic;
+
+namespace OpenBrush.Multiplayer
+{
+ public struct PendingCommand
+ {
+ public int TotalExpectedChildren;
+
+ public Guid Guid;
+ public BaseCommand Command;
+ public Action PreCommandAction;
+ public List ChildCommands;
+
+ public PendingCommand(Guid guid, BaseCommand command, Action action, int count)
+ {
+ Guid = guid;
+ Command = command;
+ PreCommandAction = action;
+ TotalExpectedChildren = count;
+ ChildCommands = new List();
+ }
+ }
+
+ public struct NetworkCommandData : INetworkStruct
+ {
+ public Guid CommandGuid;
+ public Guid ParentGuid;
+ public int ChildCount;
+
+ public NetworkCommandData(Guid commandGuid, Guid parentGuid = default, int childCount = 0)
+ {
+ CommandGuid = commandGuid;
+ ParentGuid = parentGuid;
+ ChildCount = childCount;
+ }
+ }
+
+ public struct NetworkedControlPoint : INetworkStruct
+ {
+ public Vector3 m_Pos;
+ public Quaternion m_Orient;
+
+ public const uint EXTENSIONS = (uint)(
+ SketchWriter.ControlPointExtension.Pressure |
+ SketchWriter.ControlPointExtension.Timestamp);
+ public float m_Pressure;
+ public uint m_TimestampMs; // CurrentSketchTime of creation, in milliseconds
+
+ public NetworkedControlPoint Init(PointerManager.ControlPoint point)
+ {
+ m_Pos = point.m_Pos;
+ m_Orient = point.m_Orient;
+ m_Pressure = point.m_Pressure;
+ m_TimestampMs = point.m_TimestampMs;
+
+ return this;
+ }
+
+ internal static PointerManager.ControlPoint ToControlPoint(NetworkedControlPoint networkedControlPoint)
+ {
+ var point = new PointerManager.ControlPoint
+ {
+ m_Pos = networkedControlPoint.m_Pos,
+ m_Orient = networkedControlPoint.m_Orient,
+ m_Pressure = networkedControlPoint.m_Pressure,
+ m_TimestampMs = networkedControlPoint.m_TimestampMs
+ };
+
+ return point;
+ }
+ }
+
+ [System.Serializable]
+ public struct NetworkedStroke : INetworkStruct
+ {
+ public const int k_MaxCapacity = 128;
+ public Stroke.Type m_Type;
+ [Networked][Capacity(k_MaxCapacity)] public NetworkArray m_ControlPointsToDrop => default;
+ public Color m_Color;
+ public Guid m_BrushGuid;
+ // The room-space size of the brush when the stroke was laid down
+ public float m_BrushSize;
+ // The size of the pointer, relative to when the stroke was laid down.
+ // AKA, the "pointer to local" scale factor.
+ // m_BrushSize * m_BrushScale = size in local/canvas space
+ public float m_BrushScale;
+ [Networked][Capacity(k_MaxCapacity)] public NetworkArray m_ControlPoints => default;
+
+ // Use for determining length.
+ public int m_ControlPointsCapacity;
+ // Seed for deterministic pseudo-random numbers for geometry generation.
+ // Not currently serialized.
+ public int m_Seed;
+
+ public static Stroke ToStroke(NetworkedStroke netStroke)
+ {
+ var stroke = new Stroke
+ {
+ m_Type = Stroke.Type.NotCreated,
+ m_IntendedCanvas = App.Scene.MainCanvas,
+ m_BrushGuid = netStroke.m_BrushGuid,
+ m_BrushScale = netStroke.m_BrushScale,
+ m_BrushSize = netStroke.m_BrushSize,
+ m_Color = netStroke.m_Color,
+ m_Seed = netStroke.m_Seed,
+ m_ControlPoints = new PointerManager.ControlPoint[netStroke.m_ControlPointsCapacity],
+ m_ControlPointsToDrop = new bool[netStroke.m_ControlPointsCapacity]
+ };
+
+ for (int i = 0; i < netStroke.m_ControlPointsCapacity; ++i)
+ {
+ var point = NetworkedControlPoint.ToControlPoint(netStroke.m_ControlPoints[i]);
+ stroke.m_ControlPoints[i] = point;
+ }
+
+ for (int i = 0; i < netStroke.m_ControlPointsCapacity; ++i)
+ {
+ stroke.m_ControlPointsToDrop[i] = netStroke.m_ControlPointsToDrop[i];
+ }
+
+ return stroke;
+ }
+
+ public NetworkedStroke Init(Stroke data)
+ {
+ m_Type = data.m_Type;
+ m_BrushGuid = data.m_BrushGuid;
+ m_BrushScale = data.m_BrushScale;
+ m_BrushSize = data.m_BrushSize;
+ m_Color = data.m_Color;
+ m_Seed = data.m_Seed;
+
+ m_ControlPointsCapacity = data.m_ControlPoints.Length;
+
+ for(int i = 0; i < data.m_ControlPoints.Length; i++)
+ {
+ var point = new NetworkedControlPoint().Init(data.m_ControlPoints[i]);
+ m_ControlPoints.Set(i, point);
+ }
+
+ for(int i = 0; i < data.m_ControlPointsToDrop.Length; i++)
+ {
+ m_ControlPointsToDrop.Set(i, data.m_ControlPointsToDrop[i]);
+ }
+
+ return this;
+ }
+ }
+}
+
+#endif // FUSION_WEAVER
diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs.meta b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs.meta
new file mode 100644
index 0000000000..ab108b277b
--- /dev/null
+++ b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e9097fe8b260d0b47935c11b12db0424
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/PassthroughManager.cs b/Assets/Scripts/PassthroughManager.cs
index 84621e649e..3663d5c0a6 100644
--- a/Assets/Scripts/PassthroughManager.cs
+++ b/Assets/Scripts/PassthroughManager.cs
@@ -1,5 +1,17 @@
-using System.Collections;
-using System.Collections.Generic;
+// Copyright 2022-2023 The Open Brush Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
using UnityEngine;
namespace TiltBrush
@@ -9,10 +21,9 @@ public class PassthroughManager : MonoBehaviour
void Start()
{
#if OCULUS_SUPPORTED
- var passthrough = gameObject.AddComponent();
- passthrough.overlayType = OVROverlay.OverlayType.Underlay;
-#endif // OCULUS_SUPPORTED
+ var passthrough = gameObject.AddComponent();
+ passthrough.overlayType = OVROverlay.OverlayType.Underlay;
+#endif // OCULUS_SUPPORTED
}
}
}
-
diff --git a/Assets/Scripts/PointerManager.cs b/Assets/Scripts/PointerManager.cs
index 2a01901a87..95fda8ab87 100644
--- a/Assets/Scripts/PointerManager.cs
+++ b/Assets/Scripts/PointerManager.cs
@@ -106,6 +106,7 @@ public struct ColorShiftComponentSetting
// The layout should match the most commonly-seen layout in the binary file.
// See SketchMemoryScript.ReadMemory.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
+ [System.Serializable]
public struct ControlPoint
{
public Vector3 m_Pos;
@@ -198,6 +199,8 @@ public Transform SymmetryWidget
/// active simultaneously. eg, 4-way symmetry is not allowed during timeline edit mode;
/// floating-panel mode doesn't actually _use_ the Wand's pointer, etc.
private PointerData[] m_Pointers;
+
+ private List m_RemoteUserPointers;
private bool m_InPlaybackMode;
private PointerData m_MainPointerData;
@@ -388,6 +391,15 @@ public PointerScript GetTransientPointer(int i)
return m_Pointers[NumUserPointers + i].m_Script;
}
+ public PointerScript CreateRemotePointer()
+ {
+ GameObject obj = (GameObject)Instantiate(m_AuxPointerPrefab, transform, true);
+ var script = obj.GetComponent();
+ script.ChildIndex = m_RemoteUserPointers.Count - 1;
+ m_RemoteUserPointers.Add(script);
+ return script;
+ }
+
/// The brush size, using "normalized" values in the range [0,1].
/// Guaranteed to be in [0,1].
public float GetPointerBrushSize01(InputManager.ControllerName controller)
@@ -469,6 +481,7 @@ void Awake()
Debug.Assert(m_MaxPointers > 0);
m_Pointers = new PointerData[m_MaxPointers];
+ m_RemoteUserPointers = new List();
m_CustomMirrorMatrices = new List();
for (int i = 0; i < m_Pointers.Length; ++i)
@@ -609,6 +622,11 @@ void Update()
SetPointersRenderingEnabled(false);
DisablePointerPreviewLine();
}
+
+ for (int i = 0; i < m_RemoteUserPointers.Count; ++i)
+ {
+ m_RemoteUserPointers[i].UpdatePointer();
+ }
}
public void StoreBrushInfo()
diff --git a/Assets/Scripts/SecretsConfig.cs b/Assets/Scripts/SecretsConfig.cs
index b3f9fa2c32..6b494ceabf 100644
--- a/Assets/Scripts/SecretsConfig.cs
+++ b/Assets/Scripts/SecretsConfig.cs
@@ -25,7 +25,8 @@ public enum Service
Sketchfab = 1,
Oculus = 2,
OculusMobile = 3,
- Pimax = 4
+ Pimax = 4,
+ PhotonFusion = 5,
}
[Serializable]
diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs
index 1e85ff1a53..992e290168 100644
--- a/Assets/Scripts/SketchMemoryScript.cs
+++ b/Assets/Scripts/SketchMemoryScript.cs
@@ -36,6 +36,9 @@ public class SketchMemoryScript : MonoBehaviour
public static SketchMemoryScript m_Instance;
public event Action OperationStackChanged;
+ public Action CommandPerformed;
+ public Action CommandUndo;
+ public Action CommandRedo;
public GameObject m_UndoBatchMeshPrefab;
public GameObject m_UndoBatchMesh;
@@ -340,7 +343,7 @@ void Update()
PerformAndRecordCommand(m_RepaintStrokeParent);
m_RepaintStrokeParent = null;
}
- OperationStackChanged();
+ OperationStackChanged?.Invoke();
}
}
@@ -371,7 +374,7 @@ public Stroke DuplicateStroke(Stroke srcStroke, CanvasScript canvas, TrTransform
return duplicate;
}
- public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged = false)
+ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged = false, bool invoke = true)
{
SketchSurfacePanel.m_Instance.m_LastCommand = command;
bool discardCommand = discardIfNotMerged;
@@ -395,7 +398,12 @@ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged
}
delta.Redo();
m_OperationStack.Push(command);
- OperationStackChanged();
+ OperationStackChanged?.Invoke();
+
+ if (invoke)
+ {
+ CommandPerformed?.Invoke(command);
+ }
}
// TODO: deprecate in favor of PerformAndRecordCommand
@@ -414,7 +422,8 @@ public void RecordCommand(BaseCommand command)
command = top;
}
m_OperationStack.Push(command);
- OperationStackChanged();
+ OperationStackChanged?.Invoke();
+ CommandPerformed?.Invoke(command);
}
/// Returns approximate latest timestamp from the stroke list (including deleted strokes).
@@ -788,7 +797,7 @@ public void ClearMemory()
}
}
m_OperationStack.Clear();
- if (OperationStackChanged != null) { OperationStackChanged(); }
+ OperationStackChanged?.Invoke();
m_LastOperationStackCount = 0;
m_MemoryList.Clear();
App.GroupManager.ResetGroups();
@@ -904,20 +913,30 @@ public IEnumerator RepaintCoroutine()
m_RepaintCoroutine = null;
}
- public void StepBack()
+ public void StepBack(bool invoke = true)
{
var comm = m_OperationStack.Pop();
comm.Undo();
m_RedoStack.Push(comm);
- OperationStackChanged();
+ OperationStackChanged?.Invoke();
+
+ if (invoke)
+ {
+ CommandUndo?.Invoke(comm);
+ }
}
- public void StepForward()
+ public void StepForward(bool invoke = true)
{
var comm = m_RedoStack.Pop();
comm.Redo();
m_OperationStack.Push(comm);
- OperationStackChanged();
+ OperationStackChanged?.Invoke();
+
+ if (invoke)
+ {
+ CommandRedo?.Invoke(comm);
+ }
}
public static IEnumerable AllStrokes()
diff --git a/Assets/Scripts/Stroke.cs b/Assets/Scripts/Stroke.cs
index 796a840b80..209e5c1886 100644
--- a/Assets/Scripts/Stroke.cs
+++ b/Assets/Scripts/Stroke.cs
@@ -20,7 +20,7 @@
namespace TiltBrush
{
-
+ [System.Serializable]
public class Stroke : StrokeData
{
public enum Type
diff --git a/Assets/Scripts/StrokeData.cs b/Assets/Scripts/StrokeData.cs
index b0921dce36..26ec4f41a3 100644
--- a/Assets/Scripts/StrokeData.cs
+++ b/Assets/Scripts/StrokeData.cs
@@ -17,7 +17,7 @@
namespace TiltBrush
{
-
+ [System.Serializable]
public class StrokeData
{
public Color m_Color;
diff --git a/Assets/Scripts/VrSdk.cs b/Assets/Scripts/VrSdk.cs
index 537344916e..5018afb2d5 100644
--- a/Assets/Scripts/VrSdk.cs
+++ b/Assets/Scripts/VrSdk.cs
@@ -184,6 +184,18 @@ void Awake()
var cameraRig = m_VrSystem.AddComponent();
//Disable the OVRCameraRig's eye cameras, since Open Brush already has its own.
cameraRig.disableEyeAnchorCameras = true;
+
+ //Get Oculus ID
+ var appId = App.Config.OculusSecrets.ClientId;
+#if UNITY_ANDROID
+ appId = App.Config.OculusMobileSecrets.ClientId;
+#endif
+
+ if (Unity.XR.Oculus.Utils.GetSystemHeadsetType() != Unity.XR.Oculus.SystemHeadset.Oculus_Quest)
+ {
+ Oculus.Platform.Core.Initialize(appId);
+ }
+
#endif // OCULUS_SUPPORTED
#if PIMAX_SUPPORTED
diff --git a/Assets/XR/Settings/OpenXR Editor Settings.asset b/Assets/XR/Settings/OpenXR Editor Settings.asset
index 1cb89fc3b2..0e4babc9a7 100644
--- a/Assets/XR/Settings/OpenXR Editor Settings.asset
+++ b/Assets/XR/Settings/OpenXR Editor Settings.asset
@@ -12,5 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 975057b4fdcfb8142b3080d19a5cc712, type: 3}
m_Name: OpenXR Editor Settings
m_EditorClassIdentifier:
- Keys:
- Values: []
+ Keys: 0100000007000000
+ Values:
+ - featureSets: []
+ - featureSets: []
diff --git a/Packages/manifest.json b/Packages/manifest.json
index 15f6fae119..5b3b01af2e 100644
--- a/Packages/manifest.json
+++ b/Packages/manifest.json
@@ -1,7 +1,8 @@
{
"dependencies": {
"com.ixxy.unitysymmetry": "https://github.com/IxxyXR/unity-symmetry.git?nocache=7#upm",
- "com.meta.xr.sdk.utilities": "https://github.com/icosa-mirror/com.meta.xr.sdk.utilities.git#open-brush",
+ "com.meta.xr.sdk.platform": "56.0.0-preview",
+ "com.meta.xr.sdk.utilities": "https://github.com/icosa-mirror/com.meta.xr.sdk.core.git#56.0.0-openbrush",
"com.unity.2d.sprite": "1.0.0",
"com.unity.2d.tilemap": "1.0.0",
"com.unity.cloud.gltfast": "6.0.1",
@@ -12,6 +13,7 @@
"com.unity.inputsystem": "https://github.com/icosa-mirror/com.unity.inputsystem.git#open-brush",
"com.unity.localization": "1.4.2",
"com.unity.mobile.android-logcat": "1.3.2",
+ "com.unity.nuget.mono-cecil": "1.10.2",
"com.unity.performance.profile-analyzer": "1.2.2",
"com.unity.test-framework": "1.1.33",
"com.unity.textmeshpro": "3.0.6",
diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json
index be6f33d4e3..793c3d8c24 100644
--- a/Packages/packages-lock.json
+++ b/Packages/packages-lock.json
@@ -13,12 +13,22 @@
"dependencies": {},
"hash": "4f87195d54ccefe076678cb83a296b3f0428fc53"
},
+ "com.meta.xr.sdk.platform": {
+ "version": "56.0.0-preview",
+ "depth": 0,
+ "source": "registry",
+ "dependencies": {
+ "com.meta.xr.sdk.utilities": "56.0.0-preview",
+ "com.unity.ugui": "1.0.0"
+ },
+ "url": "https://npm.developer.oculus.com"
+ },
"com.meta.xr.sdk.utilities": {
- "version": "https://github.com/icosa-mirror/com.meta.xr.sdk.utilities.git#open-brush",
+ "version": "https://github.com/icosa-mirror/com.meta.xr.sdk.core.git#56.0.0-openbrush",
"depth": 0,
"source": "git",
"dependencies": {},
- "hash": "79e3da292d15308ab01b14e9487b8b1253dcba84"
+ "hash": "57bad66c67cd72a31898a1d29dae4d4201ef36c1"
},
"com.unity.2d.sprite": {
"version": "1.0.0",
@@ -141,6 +151,13 @@
"dependencies": {},
"url": "https://packages.unity.com"
},
+ "com.unity.nuget.mono-cecil": {
+ "version": "1.10.2",
+ "depth": 0,
+ "source": "registry",
+ "dependencies": {},
+ "url": "https://packages.unity.com"
+ },
"com.unity.nuget.newtonsoft-json": {
"version": "3.2.1",
"depth": 1,